StarkEx Perpetual Trading Playground tutorial

The StarkEx Perpetual Trading Playground is a deployment of StarkEx for Perpetual Trading 2.0 on the Sepolia testnet that provides a simple demonstration of the StarkEx scalability solution. You can use the Playground to learn StarkEx hands-on and start developing your own application on the Sepolia testnet.

On the playground, you can perform the following flows, which represent a limited collection of the Perpetual system’s full API:

  1. Deposit -
    Inserting collateral assets from an onchain wallet to an offchain position.

  2. Transfer -
    Transferring assets between two offchain positions.

  3. Trade -
    Issuing a trade between two users offchain: Trading synthetic assets, or trading collateral for synthetic assets.

  4. Withdrawal -
    Reclaiming assets from an offchain position to receive them onchain.

For a detailed breakdown of these flows, see the Gateway API in the StarkEx API Reference.

A real StarkEx instance compared with the Playground

Elements of a real StarkEx instance differ from the playground, as described in Table 1, “A real StarkEx instance compared with the Playground”.

The purpose of this Playground is to facilitate manual experimentation, but it is not optimized for, nor is it intended to be used for, automated testing. When you are ready to conduct testing, including automation, contact us at info@starkware.co to set up your own dedicated test instance.

Table 1. A real StarkEx instance compared with the Playground
A real StarkEx instance StarkEx Playground

You are the only operator. You have full control of your StarkEx instance.

There are multiple operators.

You manage the allocation of position ids as part of your application.

The position id that you choose for your transaction might already be occupied by someone else.

You are the only one sending transactions, so you shouldn’t call get_first_unused_tx_id.

StarkEx processes transactions sequentially, based on the value of each transaction’s ID, so the ids in your list of transactions must include all integers in the range of values.

For example, for a list of three transactions, the list of transaction ID values cannot be 0,1,3, it must be 0,1,2. The list can also be non-sequential, such as 0,2,1. If the list doesn’t include an integer, StarkEx stops processing when it reaches the missing ID and waits for it to be submitted.

In order to get the next transaction ID, you must query the StarkEx gateway by calling get_first_unused_tx_id, because other Playground users need to sync the next tx_id.

Be aware that StarkEx does not process two transactions with the same ID and does not process a transaction if the transaction with the previous ID has not been processed.

The time for a transaction to appear onchain after being submitted can be up to 18 hours.

The time for a transaction to appear onchain after being submitted is approximately five minutes.

StarkEx for Perpetual Trading

You must implement funding ticks.

Funding ticks are not necessary.

Running the Playground

The Playground is a StarkEx test environment running on the Sepolia 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-sepolia-v2.starkex.co/gateway

  • Feeder gateway: https://perpetual-playground-sepolia-v2.starkex.co/feeder_gateway

Prerequisites

  • An Infura endpoint link for Sepolia. If you don’t have one, please open an Ethereum account at https://infura.io/ to acquire a link.

  • A Sepolia ETH account with enough ETH to pay for the deposit transaction gas fees. For your convenience, you can try this faucet to obtain Sepolia ETH.

  • curl is installed.

Conducting transactions in the Playground

  1. 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.

  2. Make a deposit to the positions you want to play with:

    1. Send a deposit transaction onchain to the StarkEx perpetual contract.

    2. Send a deposit transaction offchain.

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.

  • In order to get the next transaction ID, you must query the StarkEx gateway by calling the get_first_unused_tx_id API, because other Playground users need to synchronize the next transaction ID.

    Be aware that StarkEx does not process two transactions with the same ID and does not process a transaction if the transaction with the previous ID has not been processed.

  • The value for positionId that you define for your transaction can be taken by someone else.

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 pair, up to the point where you issue a trade offchain. 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.

  1. Generate a Stark key pair via the Key derivation process:

    In order to interact with the offchain system, a user must have a dedicated Stark key.

    The Stark key pair consists of the private Stark key, which is used to sign transactions the owner wishes to execute, and a public Stark key, which represents the user’s address/identity offchain.

    Generate a Stark key pair using either the StarkEx Crypto SDK or the Python Crypto Library.

    • To generate a Stark key pair using the StarkEx Crypto SDK:

      keyGen = require('./src/js/key_derivation.js')
      
      starkKey = keyGen.privateToStarkKey("3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc")

      This example uses a pre-generated private Stark key example, but you can generate your own by randomizing a 62 character hex-string.

    • 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]
  2. 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 allows you to deposit the simulated collateral asset that the playground supports.

    1. Navigate to the Contract tab, and under that to Write Contract.

    2. Connect your Metamask wallet so you are able to invoke the contract’s functions.

    3. Use the selfMint function to add 10000000000000 tokens to your wallet (to send the transaction via Metamask click the Write button):
      selfMint function

    4. Use the approve function to authorize StarkEx to spend your collateral onchain. Set the StarkEx contract address (0xE616885fe8c64bB7D07a932916b7fD93cdd40a35) as the spender, and set the amount at 10000000000000:

      approve function

  3. Issue a deposit to the StarkEx contract onchain, here: https://sepolia.etherscan.io/address/0xE616885fe8c64bB7D07a932916b7fD93cdd40a35#writeContract

    This is the first in a 2-phase deposit process that gives you access to the collateral asset offchain.

    1. Go to the Write Contract tab on this contract and connect your Metamask wallet as in the previous step.

    2. Issue a deposit of collateral tokens using the deposit function:

      The function receives as input:

      • Your public Stark key (generated in step 1), represented as an integer.

      • The collateral asset ID offchain (given as the integer 1462689598990563437242498716673917341065799132144045992877344104815146517955)

      • A vault_id of your choosing, an integer representing a position to occupy offchain (should be selected at random).

      • An amount to deposit, in this case 241535140800
        deposit function

  4. Send a deposit request to the StarkEx gateway (perpetual deposit documentation):

    Any user can issue a deposit to the onchain contract (as demonstrated in the previous step), thereby locking the desired amount of funds for use offchain.

    It is only when a subsequent deposit request is sent to the gateway that the deposit event onchain is processed and (if the transaction passes the validation checks) the balance of the target position offchain is updated accordingly.

    A deposit request to the StarkEx gateway must always be preceded by a valid onchain deposit.

    1. Get the first_unused_tx_id:

      curl https://perpetual-playground-sepolia-v2.starkex.co/gateway/get_first_unused_tx_id; echo
    2. Note the response, which serves as the transaction ID, 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.

    3. Run the following command 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-sepolia-v2.starkex.co/gateway/add_transaction; echo

      The fields in this transaction should match those filled in the previous step (issuing a deposit to the onchain contract).

      • Note that the position_id is equal to the vault_id chosen in the previous step.

      • The tx_id is received using get_first_unused_tx_id and is then incremented for following transactions.

      • The public_key is your public Stark key, given as a hex string.

    4. Note the response from the gateway - you should see \{"code": "TRANSACTION_RECEIVED"} to indicate the gateway has received your transaction.

  5. 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 these validations causes the Perpetual playground to disregard this transaction

    • As a future-operator, you have control over the policy regarding failed transactions.

    1. Query the feeder-gateway for the latest batch ID:

      curl https://perpetual-playground-sepolia-v2.starkex.co/feeder_gateway/get_last_batch_id; echo

      This query gives you the most recent batch ID that is being processed by the system, which should include your previous transaction.

    2. Use the latest batch ID to get the batch info from the feeder-gateway:

      curl https://perpetual-playground-sepolia-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 an offchain balance in your selected position to interact with.

  6. Send some collateral to another position using an offchain transfer transaction (perpetual transfer doc):

    • Transfers in the Perpetual StarkEx system allow to move collateral (and only collateral) assets between positions.

    • This example transfers collateral to some predetermined position

    1. 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=
        1462689598990563437242498716673917341065799132144045992877344104815146517955,
            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 public 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("1462689598990563437242498716673917341065799132144045992877344104815146517955"/*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 public Stark key.
        sigLib.verify(keyPair, msgHash, sig)
    2. Send an add_transaction request to the StarkEx gateway, providing our transfer message payload with the signature represented by the components r and s (remember to use an updated tx_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-sepolia-v2.starkex.co/gateway/add_transaction; echo
    3. As with the deposit request submission, verify your transfer is being processed via the feeder gateway.

Congratulations, you have officially successfully transmitted a dedicated offchain 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:

  • Onchain deposit transactions' description of the StarkEx contract with Etherscan links.

  • offchain StarkEx transactions' description containing the transaction type and unique tx_id.

How do I see the onchain state update?

How can I read the offchain position balances?

Position balances derive directly from the history of the transactions that were submitted to StarkEx batches. You can query this information offchain 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 onchain 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:

      This asset’s risk factor has only this one segment:

      risk:

      214748365

      upper_bound:

      340282366920938463463374607431768211455

      External price:

      56236.782298457914212352

      Internal price:

      24153514080

    • '0x4554482d3800000000000000000000'

      Name:

      ETH

      Resolution:

      108

      Risk factor:

      This asset’s risk factor has only this one segment:

      risk:

      322122548

      upper_bound:

      340282366920938463463374607431768211455

      External price:

      245.752267153034510336

      Internal price:

      10554979503

    • '0x4c494e4b2d37000000000000000000'

      Name :

      LINK

      Resolution:

      107

      Risk factor:

      This asset’s risk factor has only this one segment:

      risk:

      429496730

      upper_bound:

      340282366920938463463374607431768211455

      External price:

      93.785511767155703808

      Internal price:

      40280570588

Collateral_asset_info:
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

I need some more guidance, can you help me?

Sure! Please contact us at info@starkware.co.