-
Notifications
You must be signed in to change notification settings - Fork 38
UDT (sudt) Operations Tutorial
Simple UDT(SUDT) script is a type script, as the RFC25 says UDT only support two operations issue and transfer. SUDT can work with any lock script, but in practice SUDT mostly work with anyone-can-pay and cheque lock script. The anyone-can-pay lock let user transfer to a cell with some amount of SUDT without require to unlock the cell. The cheque lock let user issue or transfer to some address that unneeded to provide the required CKB capacity. More details please see the rfcs of two lock scripts.
In this tutorial we will demonstrate several scenarios:
- Issue some SUDT to a cheque address
- Create a cell with SUDT type script and anyone-can-pay lock script for receiving the SUDT (
empty-sudt-acpcell) - Claim the SUDT to the new created anyone-can-pay cell
- Create another
empty-sudt-acpcell with different address - Transfer a part of the claimed SUDT to the new created
empty-sudt-acpcell
- Create a cell with SUDT type script and anyone-can-pay lock script for receiving the SUDT (
- Issue some SUDT to an anyone-can-pay address
- Transfer some SUDT to a cheque address (for claim)
- Claim the SUDT to an anyone-can-pay address
- Iransfer some SUDT to a cheque address (for withdraw)
- Withdraw the SUDT to an anyone-can-pay address
First we build the contract binaries.
This will build simple_udt and anyone_can_pay:
git clone https://github.com/nervosnetwork/ckb-production-scripts
cd ckb-production-scripts
git checkout e570c11aff3eca12a47237c21598429088c610d5
git submodule update --init --recursive
make all-via-dockerAnd the binaries are located in build/simple_udt and build/anyone_can_pay.
This will build cheque:
cargo install ckb-capsule --version=0.4.3
git clone https://github.com/nervosnetwork/ckb-cheque-script.git
cd ckb-cheque-script
git checkout 4ca3e62ae39c32cfcc061905515a2856cad03fd8
git submodule update --init --recursive
cd contracts/ckb-cheque-script/ckb-lib-secp256k1/ckb-production-scripts
git submodule update --init --recursive
cd .. && make all-via-docker
cd ../../..
capsule buildAnd the binary is located in build/release/ckb-cheque-script
We will use ckb-cli deploy subcommand to deploy the contract binaries
# Since the implementation of the subcomand PR is not merged, we will build it from source
git clone -b add-deploy-subcommand https://github.com/TheWaWaR/ckb-cli.git
cd ckb-cli
make prod
ln -s $PWD/target/release/ckb-cli $HOME/.cargo/bin/ckb-cli-deployThen we copy the binaries to a direcotry and create a deployment config file.
mkdir -p deploy-files/{contracts,migrations}
cd deploy-files
cp ckb-production-scripts/build/simple_udt ckb-production-scripts/build/anyone_can_pay ckb-cheque-script/build/release/ckb-cheque-script contracts
touch deployment.tomlAnd the deployment.toml will be like this:
[[cells]]
name = "cheque_lock"
enable_type_id = true
location = { file = "contracts/ckb-cheque-script" }
[[cells]]
name = "acp_lock"
enable_type_id = true
location = { file = "contracts/anyone_can_pay" }
[[cells]]
name = "simple_udt"
enable_type_id = true
location = { file = "contracts/simple_udt" }
# reference to on-chain cells, this config is referenced by dep_groups.cells
[[cells]]
name = "secp256k1_data"
enable_type_id = false
location = { tx_hash = "<genesis-cellbase-tx-hash>", index = 3 }
# Dep group cells
[[dep_groups]]
name = "cheque_lock_dep"
cells = [
"secp256k1_data",
"cheque_lock"
]
[[dep_groups]]
name = "acp_lock_dep"
cells = [
"secp256k1_data",
"acp_lock"
]
# Replace with your own lock if you want to unlock deployed cells.
# For example the secp256k1 lock
[lock]
code_hash = "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"
args = "<sighash-address-lock-args>"
hash_type = "type"
# For unlocking inputs with multisig lock script
[multisig_config]
sighash_addresses = [
# "ckt1qyq111111111111111111111111111111111111111",
# "ckt1qyq222222222222222222222222222222222222222",
# "ckt1qyq333333333333333333333333333333333333333",
]
require_first_n = 1
threshold = 2The genesis-cellbase-tx-hash can get from ckb-cli util genesis-scripts in the output's secp256k1_data field.
The sighash-address-lock-args the your ckb-cli account id.
Then we run follow command to deploy the binaries:
ckb-cli-deploy deploy gen-txs \
--deployment-config ./deployment.toml \
--migration-dir ./migrations \
--from-address <your-account> \
--info-file ./info.json \
--sign-now
ckb-cli-deploy deploy apply-txs --migration-dir ./migrations --info-file ./info.jsonThe cell_deps.json is a config file to tell ckb-cli the script id (code_hash + hash_type) of a type of script so we can build the script by given args, and also the cell_dep information of the script id. Currently there are only 3 kind of cell_dep are allowed:
-
acpfor anyone-can-pay lock script -
chequefor cheque lock script -
sudtfor simple udt type script
Below is an example content:
{
"items": {
"cheque": {
"script_id": {
"hash_type": "type",
"code_hash": "0xb62470c53fe232c8120f81bd65d27d7d88f1e2bfd5e493d0219a5afd5be3b962"
},
"cell_dep": {
"out_point": {
"tx_hash": "0x7c444551da09140abf7c8bbb956cea7d3d3feeb9676c29281bb66e505be48e99",
"index": "0x0"
},
"dep_type": "dep_group"
}
},
"acp": {
"script_id": {
"hash_type": "type",
"code_hash": "0x23c28bc9918408180105fdeb173933e8b6bbf200f8e1c286af994e38ba8d3d3e"
},
"cell_dep": {
"out_point": {
"tx_hash": "0x7c444551da09140abf7c8bbb956cea7d3d3feeb9676c29281bb66e505be48e99",
"index": "0x1"
},
"dep_type": "dep_group"
}
},
"sudt": {
"script_id": {
"hash_type": "type",
"code_hash": "0x662e860980e557e3f3b1fb25cf19729bb86fcbb4344f80746843c6712439c5f0"
},
"cell_dep": {
"out_point": {
"tx_hash": "0x0cd76992bbc022a7fd37dc8a1765b106b4b42551bd37c242897bdc2e02350fed",
"index": "0x2"
},
"dep_type": "code"
}
}
}
}The actual field value should be from deploy-files/migrations/yyyy-mm-dd-xxxx.json
Here we need 3 accounts, one is for SUDT owner, and the other two is for normal SDUT account.
mkdir /tmp/tmp-ckb-cli-home
export CKB_CLI_HOME=/tmp/tmp-ckb-cli-home
# account owner
ckb-cli account new
address:
testnet: ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj
# account 1
ckb-cli account new
address:
testnet: ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85
# account 2
ckb-cli account new
address:
testnet: ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcteThen we transfer some CKB to those addresses.
ckb-cli wallet transfer --from-account <miner-address> --to-address ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj --capacity 10000.0
ckb-cli wallet transfer --from-account <miner-address> --to-address ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85 --capacity 10000.0
ckb-cli wallet transfer --from-account <miner-address> --to-address ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcte --capacity 10000.0Issue 2000 SUDT to a cheque address which the sender is the sudt owner and the receiver is account 1.
ckb-cli sudt issue \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--udt-to ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85:2000 \
--to-cheque-address \
--cell-deps ./cell_deps.jsonThe output:
receivers:
- address: ckt1qzmzgux98l3r9jqjp7qm6ewj047c3u0zhl27fy7syxd94l2muwukyqfydu2yhskr8feutg9wdvltwqtf3vey5uy2s8u8hdcakd5me8tesxl5c22f0h37sxs5xgzjg
amount: "2000"
transaction-hash: 0xefe513ae065293c0cce8a962f9c7a0386eaa1d1495e91d93611183dbb1119562The address is the cheque address. We can use this address to query the amount.
ckb-cli sudt get-amount \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--address ckt1qzmzgux98l3r9jqjp7qm6ewj047c3u0zhl27fy7syxd94l2muwukyqfydu2yhskr8feutg9wdvltwqtf3vey5uy2s8u8hdcakd5me8tesxl5c22f0h37sxs5xgzjg \
--cell-deps ./cell_deps.jsonThe output:
cell_count: 1
cells:
- amount: "2000"
out_point:
index: 0x0
tx_hash: 0xefe513ae065293c0cce8a962f9c7a0386eaa1d1495e91d93611183dbb1119562
total_amount: "2000"Then we create a cell with SUDT type script and anyone-can-pay lock script (account 1) for receiving the SUDT.
ckb-cli sudt new-empty-acp \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--to ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85 \
--cell-deps ./cell_deps.json The output:
acp-address: ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw
transaction-hash: 0xf6f37d3b777a318326d92b5496f28f6d3a4477554de09885172ff45e9a33ecbcQuery the amount of the new created anyone-can-pay cell.
ckb-cli sudt get-amount \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--address ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw \
--cell-deps ./cell_deps.jsonThe output:
cell_count: 1
cells:
- amount: "0"
out_point:
index: 0x0
tx_hash: 0xf6f37d3b777a318326d92b5496f28f6d3a4477554de09885172ff45e9a33ecbc
total_amount: "0"Claim the SUDT to the new created anyone-can-pay-cell
ckb-cli sudt cheque-claim \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--receiver ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85 \
--cell-deps ./cell_deps.jsonQuery the SUDT amount of account 1.
ckb-cli sudt get-amount \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--address ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw \
--cell-deps ./cell_deps.jsonThe output:
cell_count: 1
cells:
- amount: "2000"
out_point:
index: 0x0
tx_hash: 0xf29806ea07769381e20479319611a9717c00a09e1cc3a02cf27a25073efa86f9
total_amount: "2000"Then we create another anyone-can-pay cell (account 2), the output:
acp-address: ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuq2swumd37vvw70wgu556hgxrk025rdls4sn8j5qk
transaction-hash: 0x11a370d042e79f7b48ce2f25d1306a4af91dc15722568873ce7a4efac8ff604bTransfer a part of the claimd SUDT to new cell.
ckb-cli sudt transfer \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw \
--udt-to ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuq2swumd37vvw70wgu556hgxrk025rdls4sn8j5qk:600 \
--to-acp-address \
--cell-deps ./cell_deps.jsonThen if we query the two above anyone-can-pay address the amount will be:
- account 1: 1400
- account 2: 600
Issue 300 SUDT to account 1's anyone-can-pay address
ckb-cli sudt issue \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--udt-to ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw:300 \
--to-acp-address \
--cell-deps ./cell_deps.jsonThen if we query the account 1's amount, it become 1700.
Transfer 500 SUDT from account 1 anyone-can-pay address to account 2 cheque address:
ckb-cli sudt transfer \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw \
--udt-to ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcte:500 \
--to-cheque-address \
--cell-deps ./cell_deps.jsonThen if we query the two above anyone-can-pay address the amount will be:
- account 1: 1200
- account 2: 600
Claim the SUDT to account 2:
ckb-cli sudt cheque-claim \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85 \
--receiver ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcte \
--cell-deps ./cell_deps.jsonQuery two account's amounts again:
- account 1: 1200
- account 2: 1100
Transfer 500 SUDT from account 1 anyone-can-pay address to account 2 cheque address:
ckb-cli sudt transfer \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qq3u9z7fjxzqsxqpqh77k9eex05tdwljqruwrs5x47v5uw96357nuqveg0uxzw7j84zkxyn9enh3nfhdla76cngw3fvxw \
--udt-to ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcte:500 \
--to-cheque-address \
--cell-deps ./cell_deps.jsonQuery two account's amounts:
- account 1: 700
- account 2: 1100
Then we will withdraw after 6 epochs (10800 blocks):
# First change ckb-miner.toml miner.workers.value = 1
ckb miner -l 10800Withdraw the SUDT amount:
ckb-cli sudt cheque-withdraw \
--owner ckt1qyq86vaa6e8tsruv5ngcd5tp7lcvcewxy7cquuksvj \
--sender ckt1qyqfjslcvyaay029vvfxtn80rxnwmlma43xscrqn85 \
--receiver ckt1qyq9qaekmruccau7u3eff4wsv8v74gxmlptqj2lcte \
--to-acp-address \
--cell-deps ./cell_deps.jsonQuery two account's amounts:
- account 1: 1200
- account 2: 1100