StarkEx Perpetual Trading Playground tutorial
The StarkEx Perpetual Trading Playground is a StarkEx deployment on the Goerli testnet. You can use the Playground to learn the ropes hands-on and start developing your own Application on the Goerli testnet.
On the playground, you will be able to perform the following flows, which represent a limited collection of the Perpetual system’s full API:
-
Deposit -
Inserting collateral assets from a L1 wallet to a L2 position. -
Transfer -
Transferring assets between two L2 positions. -
Trade -
Issuing a trade between two users on L2: Trading synthetic assets, or trading collateral for synthetic assets. -
Withdrawal -
Reclaiming assets from an L2 position to receive them on L1.
For a detailed breakdown of these flows, see the Gateway API in the StarkEx API Reference.
Running the Playground
The Playground is a StarkEx test environment running on the Goerli testnet. Use the curl
command to issue requests to the Playground’s gateway.
Use the following URLs to interact with the test environment via the StarkEx API:
-
Gateway:
https://perpetual-playground-v2.starkex.co
-
Feeder gateway:
https://perpetual-playground-v2.starkex.co/feeder_gateway
Prerequisites
-
An Infura endpoint link for Goerli. If you don’t have one, please open an Ethereum account at https://infura.io/ to acquire a link.
-
A Goerli ETH account with enough ETH to pay for the deposit transaction gas fees. For your convenience, you can try this faucet to obtain Goerli ETH.
-
curl
is installed.
Conducting transactions in the Playground
-
Generate a Stark key pair using the Creating a Stark key process, detailed in the Hands-on demonstration below.
In order to send your own transactions, you must sign transactions with a STARK signature, which requires your own Stark key pair. You can use the StarkEx Crypto SDK or the Python Crypto Library.
-
Make a deposit to the positions you want to play with:
-
Send a deposit transaction on L1 to the StarkEx perpetual contract.
-
Send a deposit transaction on L2.
-
Now, you can perform any of the following transaction flows:
These are all explained in Hands-on demonstration, below.
For more information, on the corresponding APIs, see the StarkEx Perpetual Trading API Reference.
|
Hands-on demonstration
The following is a hands-on guide for newcomers to the StarkEx Perpetual Trading system, which leads you step-by-step from the initial starting point of generating your own Stark key, up to the point where you issue a trade on L2. If you are familiar with the workings on the Perpetual system, or simply wish to skip this section and explore the system on your own, see General configuration.
-
Generate a Stark key via the Key derivation process:
In order to interact with the L2 system, a user must have a dedicated Stark key.
The Stark key is divided into two components: the private Stark key, which is used to sign transactions the owner wishes to execute, and a public Stark key (often referred to simply as “the Stark key”) which represents the user’s address/identity on L2.
Generate a Stark key using either the StarkEx Crypto SDK or the Python Crypto Library.
-
To generate a Stark key using the StarkEx Crypto SDK:
This example uses a pre-generated Stark private key example, but you can generate your own by randomizing a 62 character hex-string.
keyGen = require('./src/js/key_derivation.js') starkKey = keyGen.privateToStarkKey("3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc")
-
To generate a Stark key using the Python Crypto Library:
from starkware.crypto.signature.signature import * private_stark_key = get_random_private_key() stark_key = private_key_to_ec_point_on_stark_curve(priv_key)[0]
-
-
Mint collateral and approve the StarkEx contract as a spender via the: mock ERC-20 contract.
This is a precursor to the deposit phase and will allow you to deposit the simulated collateral asset the perpetual playground supports.
-
Navigate to the Contract tab, and under that to Write Contract.
-
Connect your Metamask wallet so you are able to invoke the contract’s functions.
-
Use the
selfMint
function to add 10000000000000 tokens to your wallet (to send the transaction via Metamask click the Write button):
-
Use the
approve
function to authorize StarkEx to spend your collateral on L1. Set the StarkEx contract address (0x98271F68335189DCb5bd94DE50cA3ae14DE0dc60
) as the spender, and set the amount at10000000000000
:
-
-
Issue a deposit to the StarkEx contract on L1, here: https://goerli.etherscan.io/writecontract/index?m=normal&v=21.10.1.1&a=0x98271F68335189DCb5bd94DE50cA3ae14DE0dc60&p=0x1464553a75F805b9F67F73012B07B544e76d4b21&n=goerli.
This is the first in a 2-phase deposit process which will give you access to the collateral asset on L2.
-
Go to the *Write Contract * tab on this contract and connect your Metamask wallet as in the previous step.
-
Issue a deposit of collateral tokens using the
deposit
function:The function receives as input:
-
Your Stark key (generated in step 1), represented as an integer.
-
The collateral asset ID on L2 (given as the integer
286442224669982855773917167725901379555005478797788066723536016706544965407
) -
A
vault_id
of your choosing, an integer representing a position to occupy on L2 (should be selected at random). -
An amount to deposit, in this case
241535140800
-
-
-
Send a deposit request to the StarkEx gateway (perpetual deposit documentation):
Any user can issue a deposit to the L1 contract (as demonstrated in the previous step), thereby locking the desired amount of funds for use on L2.
It is only when a subsequent deposit request is sent to the gateway that the deposit event on L1 is processed and (if the transaction passes the validation checks) the balance of the target position on L2 is updated accordingly.
A deposit request to the StarkEx gateway must always be preceded by a valid L1 deposit.
-
Get the first_unused_tx_id:
curl https://perpetual-playground-v2.starkex.co/get_first_unused_tx_id; echo
-
Note the response, which will serve as our
tx_id
in the following transaction.-
Transactions sent to the StarkEx gateway must be serialized.
-
The above method allows us to get the next available transaction ID which we can assign to our following deposit request.
-
-
Run the following cmd to send a deposit request to the StarkEx gateway:
curl -d '{"tx_id":<tx_id>,"tx":{"position_id":"<vault_id>","public_key":"<stark_key (in hex format)>","amount":"241535140800","type":"DEPOSIT"}}' https://perpetual-playground-v2.starkex.co/add_transaction; echo
The fields in this transaction should match those filled in the previous step (issuing a deposit to the L1 contract).
-
Note that the
position_id
is equal to thevault_id
chosen in the previous step. -
The
tx_id
is received usingget_first_unused_tx_id
and is then incremented for following transactions. -
The
public_key
is your public Stark key, given as a hex string.
-
-
Note the response from the gateway - you should see
\{"code": "TRANSACTION_RECEIVED"}
to indicate the gateway has received your transaction.
-
-
Verify your deposit transaction is being processed. For more information, see the Feeder Gateway in the StarkEx Perpetual v2.0 REST API Reference:
-
A transaction received by the gateway isn’t guaranteed to be processed.
-
A number of validation checks are run before a transaction is included in the next batch, and failing this validations will make the Perpetual playground disregard this transaction
-
As a future-operator, you will have control over the policy regarding failed transactions.
-
Query the feeder-gateway for the latest batch ID:
curl https://perpetual-playground-v2.starkex.co/feeder_gateway/get_last_batch_id; echo
This will give you the most recent batch ID that is being processed by the system, which should include your previous transaction.
-
Use the latest batch ID to get the batch info from the feeder-gateway:
curl https://perpetual-playground-v2.starkex.co/feeder_gateway/get_batch_info?batch_id=<latest batch_id>; echo
-
Look for your deposit in the list of recorded transactions (identified by its uniquely assigned
tx_id
). -
If the transaction isn’t in the list you may try querying a previous batch ID (if all transactions in the batch you are currently reviewing have a greater
tx_id
than your transaction. -
If your transaction doesn’t appear and there is a different transaction occupying the
tx_id
you assigned this may be an indication your transaction has failed. In this case go back to step 3 and retry. -
If your transaction appears in the batch then this means you should now already have L2 balance in your selected position to interact with.
-
-
-
Send some collateral to another position using a L2 transfer transaction (perpetual transfer doc):
-
Transfers in the Perpetual StarkEx system allow to move collateral (and only collateral) assets between positions.
-
For this example we will transfer collateral to some predetermined position
-
First we need to generate a signature for our desired transfer message:
-
Using
perpetual_messages.py
:# Generate message hash to sign. transfer_msg_hash = get_transfer_msg( asset_id= 286442224669982855773917167725901379555005478797788066723536016706544965407, asset_id_fee=0, receiver_public_key= 2825868930652315540133093693348064822157481518754303749560050236804923333506, sender_position_id=<insert position_id>, receiver_position_id=4, src_fee_position_id=<insert position_id>, nonce=0, amount=3, max_amount_fee=0, expiration_timestamp=20000000) # Sign message with previously generated private key. r, s = sign( msg_hash=transfer_msg_hash, priv_key=priv_key) # Verify signature matches message and stark key. verify( msg_hash=transfer_msg_hash, r=r, s=s, public_key=stark_key) # Display signature components as hex. hex(r) hex(s)
-
Using
perpetual_messages.js
:# Generate message hash to sign. const perpetualMsgs = require('./perpetual_messages.js') msgHash = perpetualMsgs.getPerpetualTransferMessage("286442224669982855773917167725901379555005478797788066723536016706544965407"/*assetId*/, "0"/*assetIdFee*/, "2825868930652315540133093693348064822157481518754303749560050236804923333506"/*receiverPublicKey*/, "<insert position_id>"/*senderPositionId*/, "4"/*receiverPositionId*/, "<insert position_id>"/*srcFeePositionId*/, "0"/*nonce*/, "3"/*amount*/, "0"/*maxAmountFee*/, "20000000"/*expirationTimestamp*/) # Sign message with previously generated private key. const sigLib = require('./src/js/signature.js'); sig = sigLib.sign(keyPair, msgHash) # Verify signature matches message and stark key. sigLib.verify(keyPair, msgHash, sig)
-
-
Send an
add_transaction
request to the StarkEx gateway, providing our transfer message payload with the signature represented by the componentsr
ands
(remember to use an updatedtx_id
):curl -d '{"tx_id":tx_id,"tx":{"amount":"3","asset_id":"0xa21edc9d9997b1b1956f542fe95922518a9e28ace11b7b2972a1974bf5971f","expiration_timestamp":"20000000","nonce":"0","receiver_position_id":"4","receiver_public_key":"0x63f62982fa598ad7c4e469870b3a1c23316ba8fb717aa2b1ee3114283fb1b82","sender_position_id":"<your position_id>","sender_public_key":"<your public stark_key>","signature":{"r":"<your r>","s":"<your s>"}, "type":"TRANSFER"}}' https://perpetual-playground-v2.starkex.co/add_transaction; echo
-
As with the deposit request submission, verify your transfer is being processed via the feeder gateway.
-
Congratulations, you have officially successfully transmitted a dedicated L2 transaction in the Perpetual Playground system. You may now use the remaining funds you deposited in the previous steps to play around freely with the system - send more transfers, execute trades, and learn by experiment!
Analyzing the output
The output includes two types:
-
On-chain deposit transactions' description of the StarkEx contract with Etherscan links.
-
Off-chain StarkEx transactions' description containing the transaction type and unique tx_id.
How do I see the on-chain state update?
-
You can check the state in the StarkEx Perpetual Playground smart contract at https://goerli.etherscan.io/address/0x98271F68335189DCb5bd94DE50cA3ae14DE0dc60 . If your batch is already submitted on-chain (allow about 5 minutes), you will see a
StateUpdate
transaction. -
You can check what happened in the Verifier smart contract, which verifies STARK proofs, at https://goerli.etherscan.io/address/0x8f97970aC5a9aa8D130d35146F5b59c4aef57963.
You will see that a new proof was submitted for verification. Once the proof is verified, a new Fact is registered on-chain approving the proof validation. The StarkEx contract checks this Fact before updating the state.
How can I read the off-chain Position balances?
Position balances derive directly from the history of the transactions that were submitted to StarkEx batches. You can query this information off-chain by using the Feeder Gateway StarkEx Perpetual Trading API.
You cannot query the last batch until it has been created. So it is recommended to query the last batch only when you can track the on-chain state update. |
General configuration
The playground consists of the following components:
-
Collateral asset
Name: SelfService
ERC20 Address: 0xd44BB808bfE43095dBb94c83077766382D63952a
Asset_id: '0xa21edc9d9997b1b1956f542fe95922518a9e28ace11b7b2972a1974bf5971f'
Resolution: 106
-
Three synthetic assets
-
'0x4254432d3130000000000000000000'
Name: BTC
Resolution: 1010
Risk factor: 0.05
External price: 56236.782298457914212352
Internal price: 24153514080
-
'0x4554482d3800000000000000000000'
Name: ETH
Resolution: 108
Risk factor: 0.75
External price: 245.752267153034510336
Internal price: 10554979503
-
'0x4c494e4b2d37000000000000000000'
Name : LINK
Resolution: 107
Risk factor: 0.1
External price: 93.785511767155703808
Internal price: 40280570588
-
asset_id: '0xa21edc9d9997b1b1956f542fe95922518a9e28ace11b7b2972a1974bf5971f'
resolution: '0xf4240'
fee_position_info:
position_id: '123456'
public_key:
'0x811921ddf8b35b688ede6d94a16293bddabb2362'
orders_tree_height: 64
positions_tree_height: 64
synthetic_assets_info:
'0x4254432d3130000000000000000000':
resolution: '0x2540be400'
risk_factor: '214748365'
'0x4554482d3800000000000000000000':
resolution: '0x5f5e100'
risk_factor: '322122548'
'0x4c494e4b2d37000000000000000000':
resolution: '0x989680'
risk_factor: '429496730'
In a real StarkEx instance the general configuration includes more details regarding the oracle and funding tick. |
Oracle price tick
{
"tx_id":1,
"tx":{
"type":"ORACLE_PRICES_TICK",
"oracle_prices":{
"0x4254432d3130000000000000000000":{
"signed_prices":{
"0x60de32cdaf78bdab488d87f7720d5f3898f16d1461203621731f80fa85c35ff":{
"timestamped_signature":{
"timestamp":"1597468993",
"signature":{
"r":"0x7612fff3397a4b1f8ca578613c5540b46e308d948009d88a0bed9f7192997e8",
"s":"0x5974522f5844b7f010b0a21772ad77aef59582fb3cb2f263650e60baa844fdf"
}
},
"external_asset_id":"0x425443555344000000000000000000004d616b6572",
"price":"56236782298457914212352"
}
},
"price":"24153514080"
},
"0x4554482d3800000000000000000000":{
"signed_prices":{
"0x793e9f602abba40f00411d6320287fb4d180c06c99f768d3cf91b613f3c8810":{
"timestamped_signature":{
"timestamp":"1511954808",
"signature":{
"r":"0x1ba6a1601eacfe82c195cc234d4f0e6100afb66265b8b768907aacaf63c42b3",
"s":"0x3c6b4e4ca06a6d733e2d83d4857bab85c06b4e52fcaf1b2264a0ca01cbf1a0e"
}
},
"external_asset_id":"0x455448555344000000000000000000004d616b6572",
"price":"245752267153034510336"
}
},
"price":"10554979503"
},
"0x4c494e4b2d37000000000000000000":{
"signed_prices":{
"0x636b2c4f887da6f50dee35f21d0582b4944fe8dd1e83f61e63dabeff3e4846f":{
"timestamped_signature":{
"timestamp":"1480980993",
"signature":{
"r":"0x7f1887fba5d66e13aba37c8859531cc1f90dd59fc06479f86fab6e57ca4615c",
"s":"0x776545d06ae5fdf4102a07366efe43775f4977ba8d89cea9612ce6e9b7c2ca8"
}
},
"external_asset_id":"0x4c494e4b5553440000000000000000004d616b6572",
"price":"93785511767155703808"
}
},
"price":"40280570588"
}
},
"timestamp":"1667993252"
}
}
Additional resources
-
Gateway API in the StarkEx Perpetual Trading REST API Reference
-
Feeder Gateway API in the StarkEx Perpetual Trading REST API Reference