StarkEx Specific Concepts

Quantization and Resolution

In the Ethereum blockchain, the amounts of tokens are represented by a 256-bit number. However, for reasons of efficiency, all the amounts in StarkEx are represented by a 64-bit number.

Since the inputs to all the off-chain transactions and some on-chain requests are given in StarkEx representation, it is crucial that the code that generates the transaction signature uses the correct quantization/resolution. Therefore, the quantization/resolution is reflected in the assetId, i.e. a unique StarkEx asset representation, as we explain in detail below.

In this section, we explain how to convert between one representation to the other.

Quantization

The quantization factor (sometimes referred to as quantum) represents the multiplier of a StarkEx amount of tokens to get the blockchain amount. Namely, if the quantization of an asset is qq it means that vv tokens in StarkEx represent qβˆ—vq*v tokens in blockchain representation.

For example, say that the quantization of ETH in StarkEx is 10,000,000. (Note: In blockchain, ETH amount is represented by WEI, 1 ETH = 10βˆ’1810^{-18}WEI). This means that '1' in our system represents 10,000,000 WEI or 10βˆ’11 10^{-11} ETH on blockchain.

The quantization factor is necessary for every asset that can be represented on-chain. This is clear when looking at the deposit flow. The on-chain deposit transaction gets an unquantized amount of tokens from the user, yet the parameter to the on-chain call is a quantized amount.

Resolution

The resolution factor represents the reverse direction from quantization. That is, instead of asking "how many WEI are represented by writing '1' in the system" we ask: "what is the number in the system that represents 1 ETH" .

As an example, say that the resolution of ETH is 10,000,000. This means that 10,000,000 tokens in our system represent 1ETH, therefore 1 in the system is 10βˆ’710^{-7}ETH.

In perpetual trading, we use resolution, rather than quantization, for all the synthetic assets. Notice that since these assets are not represented on-chain, quantization is not needed. However, resolution provides the following benefits:

  • Since signed external Oracle prices are given in round units, for example, BTC/ETH and not Satoshi/WEI, it makes the translation to the price used in the system, sound.

  • Since users usually refer to buying/selling in round units, for example, BTC and not satoshi, it makes the system easier to understand.

AssetInfo, AssetType and AssetId

Inside StarkEx and independently of their standard (ETH/ERC20/ERC721/Synthetics), all assets have the same APIs.

In the off-chain application, the assetId uniquely identifies the asset for all the off-chain flows (Transfer/Conditional Transfer/Limit Order/Withdrawal)

In perpetual trading, the synthetic assets are not represented on-chain and thus the off-chainassetIdrepresentation is enough for them. The assetId of a synthetic asset is a 120-bit number that represents their identity and resolution. For example, "ETH-8".encode('ascii') represents Ethereum with a resolution of 10810^8.

For on-chain assets (non-synthetic assets), the off-chain assetId is cryptographically bound to the identity of the token as represented on-chain (external ERC20/721 for deposit/withdraw goes through, correct quantization, etc), and therefore the calculation of assetId is as follows:

  1. We call assetInfo, the string concatenation of selector (explained below) and address _(if relevant)._ It enables StarkEx to redeem assets according to their initial standard.

  2. We call assetType, the 250-bit hash of assetInfo **and quantum. It enables StarkEx to convert off-chain __balance to on-chain balance

  3. We call assetId, a 250-bit number which definition depends on the asset standard. assetId is the only asset identifier off-chain.

To compute assetInfo , assetType and assetId, we need:

  1. Selector, the 4-byte constant specifying the asset standard. Either ETH, ERC20, or ERC721.

  2. Address **(if relevant), __the asset contract address

  3. Quantum **(if relevant), the multiplicative factor from the on-chain 256-bits integer balance to the 63-bits integer used as balance in StarkEx. Only amounts divisible by the quantum can be deposited in the system.

  4. TokenId **(if relevant), __the asset serial number in the context of ERC721

Computing assetInfo, assetType and assetId

Below you can find a pseudo-code of the computation. Full implementation in JS can be found here.

ETH

def getEthAssetInfo():
ETH_SELECTOR = '0x8322fff2' # '0x8322fff2' = bytes4(keccak256(β€œETH()”))
asset_info = ETH_SELECTOR
return asset_info
​
​
def getEthAssetType(quantum):
asset_info = getEthAssetInfo()
asset_type = keccak256(asset_info, quantum)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_type
​
​
def getEthAssetId(quantum):
asset_id = getEthAssetType(quantum)
return asset_id

ERC20

def getErc20AssetInfo(address):
ERC20_SELECTOR = '0xf47261b0'
# '0xf47261b0' = bytes4(keccak256('ERC20Token(address)'))
asset_info = ERC20_SELECTOR + bytes.fromhex(address[2:]).rjust(32, b'\0')
# For ERC20, asset_info is 36 bytes long
return asset_info
​
​
def getErc20AssetType(quantum, address):
asset_info = getErc20AssetInfo(address)
asset_type = keccak256(asset_info, quantum)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_type
​
​
def getErc20AssetId(quantum, address):
asset_id = getErc20AssetType(quantum, address)
return asset_id

ERC721

def getErc721AssetInfo(address):
ERC721_SELECTOR = '0x02571792'
# 0x02571792 = bytes4(keccak256('ERC721Token(address,uint256)'))
asset_info = ERC721_SELECTOR + bytes.fromhex(address[2:]).rjust(32, b'\0')
# For ERC721, asset_info is 36 bytes long.
return asset_info
​
​
def getErc721AssetType(address):
asset_info = getErc721AssetInfo(address)
asset_type = keccak256(asset_info, 1)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_type
​
​
def getErc721AssetId(token_id, address):
asset_type = getErc721AssetType(address)
asset_id = keccak256('NFT:', asset_type, token_id)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_id

Mintable ERC721

def getErcMintable721AssetInfo(address):
MINTABLE_ERC721_SELECTOR = '0xb8b86672'
# 0xb8b86672 = bytes4(keccak256('MintableERC721Token(address,uint256)'))
asset_info = MINTABLE_ERC721_SELECTOR + bytes.fromhex(address[2:]).rjust(32, b'\0')
# For Mintable ERC721, asset_info is 36 bytes long.
return asset_info
​
​
def getMintableErc721AssetType(address):
asset_info = getErcMintable721AssetInfo(address)
asset_type = keccak256(asset_info, 1)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_type
​
​
def getMintableErc721AssetId(minting_blob, address):
asset_type = getMintableErc721AssetType(address)
blob_hash = keccak256(minting_blob)
asset_id = keccak256('MINTABLE:', asset_type, blob_hash)
& 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
asset_id = asset_id
| 0x4000000000000000000000000000000000000000000000000000000000000000
return asset_id

Mintable ERC20

def getErcMintable20AssetInfo(address):
MINTABLE_ERC20_SELECTOR = '0x68646e2d'
# 0xb8b86672 = bytes4(keccak256('MintableERC20Token(address)'))
asset_info = MINTABLE_ERC20_SELECTOR + bytes.fromhex(address[2:]).rjust(32, b'\0')
# For Mintable ERC20, asset_info is 36 bytes long.
return asset_info
​
​
def getMintableErc20AssetType(address, quantum):
asset_info = getErcMintable20AssetInfo(address)
asset_type = keccak256(asset_info, quantum)
& 0x03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
return asset_type
​
​
def getMintableErc20AssetId(minting_blob, address, quantum):
asset_type = getMintableErc721AssetType(address, quantum)
blob_hash = keccak256(minting_blob)
asset_id = keccak256('MINTABLE:', asset_type, blob_hash)
& 0x0000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
asset_id = asset_id
| 0x4000000000000000000000000000000000000000000000000000000000000000
return asset_id