How to Use Shielded Smart Contracts
z-address geneneration
z-address i.e. shielded address, is the address format that TRONZ uses. It is determined by an sk
and d
key. One can publish its z-address and receive shielded transactions.
// sk: spending key => ask, nsk, ovk
// ask: spend authorizing key, 256 => ak
// nsk: proof authorizing key, 256 => nk
// ovk: outgoing viewing key, 256
// ivk: incoming viewing key, 256 => pkD
// d: diversifier, 11bytes
// pkD: the public key of the address, g_d^ivk
// pkD + d => z-addr
sk
is the hidden private key. d
is an identifier of different addresses generated from sk
, can be used to implement HD-wallets. All other keys are used in different occasions.
Take an address as an example: ztron1m445gx74mjuuyhkyru5hrx886jszfga4a7dk3mg4uarrl0cru649jz4928tm6rqul2pg645hqv5
. The address is in bech32 format. ztron1
is the fixed prefix. The remains are encoded value of pkD
and d
.
Comparing to the original TRON address format T-....
, one can call it a transparent address or T-address.
Key and z-address related APIs are:
wallet/getspendingkey
> generating sk
wallet/getexpandedspendingkey
> sk => aks, nsk, ovk
wallet/getakfromask
> ask => ak
wallet/getnkfromnsk
> nsk => nk
wallet/getincomingviewingkey
> ak, nk => ivk
wallet/getdiversifier
> generating d
wallet/getzenpaymentaddress
> ivk, d => z-addr, pkD
wallet/getnewshieldedaddress
> generate all above keys and address at once
There are 3 main tasks related to shielded transaction:
- transfer from T-address to z-address,
mint
for shielded trc20 - transfer between z-addresses,
transfer
for shielded trc20 - transfer from z-address to T-address,
burn
for shielded trc20
Shielded TRC20 Contract
constructor (address trc20ContractAddress, uint256 scalingFactorExp)
function burn(bytes32[10] input, bytes32[2] spendAuthoritySignature, uint256 rawValue, bytes32[2] bindingSignature, address payTo, bytes32[3] c)
=> burn(bytes32[10],bytes32[2],uint256,bytes32[2],address,bytes32[3]) [4d013fde]
function mint(uint256 rawValue, bytes32[9] output, bytes32[2] bindingSignature, bytes32[21] c)
=> mint(uint256,bytes32[9],bytes32[2],bytes32[21]) [855d175e]
function transfer(bytes32[10][] input, bytes32[2][] spendAuthoritySignature, bytes32[9][] output, bytes32[2] bindingSignature, bytes32[21][] c)
=> transfer(bytes32[10][],bytes32[2][],bytes32[9][],bytes32[2],bytes32[21][]) [9110a55b]
function scalingFactor() view returns (uint256)
=> scalingFactor() [ed3437f8]
function getPath(uint256 position) view returns (bytes32, bytes32[32])
=> getPath(uint256) [e1765073]
Usage
Prerequisite - a shielded TRC20 contract
The shielded contract is at https://raw.githubusercontent.com/tronprotocol/java-tron/feature/shieldedUSDT/deploy/ShieldedTRC20.sol.
It can only be compiled with solidity variation from TRON: https://github.com/tronprotocol/solidity, and the code branch is develop
.
Deploy the code with constructor parameter (TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3, 18)
. We got a shielded TRC20 address TEkQTDyZmbY6hngxdAsxzxy9r3bUNhRjdS.
Where TF17BgPaZYbz8oxbjhriubPDsA7ArKoLX3 is the TRC20 address of JST token in Nile Testnet, 1
is the exponent of the scaling factor.
Transfer to z-address - mint
Before any transferring, use the approve
method of the original TRC20 to approve the required amount of tokens to the shielded TRC20 contract.
Suppose we want to transfer tokens to the address ztron1s2s9fpf2v2l3d8mgzf7dqnfptkrlmyekvaqlw50lpf8dz8xkdgphjuxaysh4h0wvml8qzjzrv36
.
First, create rcm
:
> curl https://api.nileex.io/wallet/getrcm
{"value": "720c84c8b41b3dcfcc1d5997e196e6de99f07aefb9274e285a23c5599ea2c40a"}
Then, construct the shielded contract parameters:
{
'from_amount': '10000',
'shielded_receives': {'note': {
'value': 1000,
'payment_address': 'ztron1s2s9fpf2v2l3d8mgzf7dqnfptkrlmyekvaqlw50lpf8dz8xkdgphjuxaysh4h0wvml8qzjzrv36',
'rcm': '720c84c8b41b3dcfcc1d5997e196e6de99f07aefb9274e285a23c5599ea2c40a',
'memo': 'HEX'}},
'shielded_TRC20_contract_address': '4148c0020ff778c4090bf196e39d51b92bf5a647b1'
}
from_amount
is of string typevalue
isfrom_amount
divided byscalingFactor
Then we got the response, the only one we care is trigger_contract_input
:
{'binding_signature': '......',
'message_hash': '.....',
'parameter_type': 'mint',
'receive_description': [{'c_enc': '..........',
'c_out': '.....',
'epk': '...',
'note_commitment': '.....',
'value_commitment': '.....',
'zkproof': '.....'}],
'trigger_contract_input': '.....'
}
Concat [855d175e]
(signature of mint()) with trigger_contract_input
, we got the calling parameter.
Sign the TriggerSmartContract
transaction with the T-address and broadcast.
Transaction: https://nile.tronscan.org/#/transaction/e191f1114cbd8cbe43452b2c3141326ae4c2aba22a82ba195c9573895cbbfd84
Query incoming notes
Use wallet/scanshieldedtrc20notesbyivk
with ivk
, ak
, nk
. You can only scan 1000 blocks at once.
The returned note has a field called is_spent
.
Transfer between z-addresses - transfer
All transfers are based on note
. You can transfer 1-2 notes to 1-2 notes. The total balanced must be matched.
alpha
and rcm
are generated by wallet/getrcm
API.
Query outgoing notes
Use wallet/scanshieldedtrc20notesbyovk
with ovk
.
Is a note spent
Use wallet/isshieldedtrc20contractNoteSpent
with ak
, nk
.
Transfer from z-address to T-address - burn
Almost the same as a transfer
. The TriggerSmartContract
transaction can be broadcasted by anyone.
References
The detailed usage document is Here.
Updated over 4 years ago