Golang

Core SDK - Golang

The XpansionChain Core SDK provides convenient access to XpansionChain's APIs and smart contracts to help projects build better web3 games and marketplaces.

💡ROLLUPS THIS SDK SUPPORTS

  • XpansionChain

CONTENTS

  • Installation

  • Initialization

  • Get data (on assets, orders, past transactions, etc.)

  • Generating Stark (Layer 2) keys

  • Operations requiring user signatures

  • How do applications generate and use signers?

  • Contract requests

  • Smart contract autogeneration

  • Further documentation


📚SDK LINKS

  • SDK reference

  • Package reference

  • Code examples

  • Github repository

Installation

The supported go versions are 1.18 or above.

go get github.com/XpansionChain/imx-core-sdk-golang

Initialization

Initialize the Core SDK client with the network on which you want your application to run (see all networks available):

Select one of the following Ethereum networks XpansionChain platform currently supports.

Environment
Description

Sandbox

The default test network (currently, it is Goërli)

Mainnet

Ethereum network

import "github.com/XpansionChain/imx-core-sdk-golang/imx/api"

const alchemyAPIKey = "alchemy api key"

func main() {
    apiConfiguration := api.NewConfiguration()
    cfg := imx.Config{
        APIConfig:     apiConfiguration,
        AlchemyAPIKey: YOUR_ALCHEMY_API_KEY,
        Environment:   imx.Sandbox,
    }
    client, err := imx.NewClient(&cfg)
    if err != nil {
        log.Panicf("error in NewClient: %v\n", err)
    }
    defer c.EthClient.Close()
}

Get data (on assets, orders, past transactions, etc.)

These methods allow you to read data about events, transactions or current state on XpansionChain (layer 2). They do not require any user authentication because no state is being changed.

Examples of the types of data that are typically retrieved include:

  • Assets or details of a particular asset

  • Token balances for a particular user

  • Orders or details about a particular order

  • Historical trades and transfers

Examples

Get all collections and get assets from a particular collection:

listCollectionsRequest := c.NewListCollectionsRequest(context.TODO())
listCollectionsRequest.PageSize(2)

listCollectionsResponse, err := c.ListCollections(&listCollectionsRequest)
if err != nil {
    log.Panicf("error in ListCollections: %v\n", err)
}

collection := listCollectionsResponse.Result[0]

listAssetsRequest := c.NewListAssetsRequest(context.TODO())
listAssetsRequest.Collection(collection.Address)
listAssetsRequest.PageSize(10)

listAssetsResponse, err := c.ListAssets(&listAssetsRequest)
if err != nil {
    log.Panicf("error in ListAssets: %v\n", err)
}

Generating Stark (Layer 2) keys

Stark keys are required to transact on XpansionChain's StarkEx Layer 2. They are the equivalent of Ethereum keys on L1 and allow users to sign transactions like trade, transfer, etc.

Key registration

On XpansionChain, the goal of generating a Stark key is to register a mapping between the Stark public key and the user's Ethereum public key so that transactions requiring both L1 and L2 signers can be executed by users.

How to generate Stark keys on XpansionChain

XpansionChain provides two Stark key generation methods: | Type of Stark key generated: | User connection methods: | When to use this method: | XpansionChain tools: | | --- | --- | --- | --- | | Deterministic - generated using the user's Ethereum key as a seed (which means that the same Ethereum key will always generate the same Stark key) | Users connect with their L1 wallet (ie. Metamask), as the L2 key can simply be obtained from the L1 key. | User experience - users don't have to store or remember Stark keys. Interoperability - when generating Stark keys for a user, think about how else they will use these keys. If they will be connecting to other applications and those applications connect to users' Stark keys (L2 wallets) via an L1 wallet, then it is best that their Stark keys are generated using this method. | Link SDK Core SDK's GenerateLegacyKey() method | | Random and non-reproducible - not generated from a user's Ethereum key | Once this Stark key is registered on XpansionChain (mapped to an Ethereum key), the Stark key owner needs to know and input this. 🚨 NOTE: If this key isn't persisted and stored by the user, it cannot be recovered and a new key cannot be re-registered. | Security - a Stark key generated using this method is completely independent of an Ethereum key, so the compromise of an Ethereum key does not compromise a user's corresponding Stark key. Isolated use-case - this method is ideal for keys that are only used for one particular function, ie. in the backend of an application that allows tokens to be minted from a collection registered with this key. | Core SDK's GenerateKey() method |

Generating or retrieving a deterministic key

If your user has a Stark key that was generated using the deterministic method, the Core SDK provides a way for you to retrieve this key using the GenerateLegacyKey() method:

import { AlchemyProvider } from '@ethersproject/providers';
import { Wallet } from '@ethersproject/wallet';
import { generateLegacyStarkPrivateKey } from '@imtbl/core-sdk';

// Create Ethereum signer
const ethNetwork = 'goerli'; // Or 'mainnet'
const provider = new AlchemyProvider(ethNetwork, YOUR_ALCHEMY_API_KEY);
const ethSigner = new Wallet(YOUR_PRIVATE_ETH_KEY).connect(provider);

// Get the legacy Stark private key
const starkPrivateKey = generateLegacyStarkPrivateKey(ethSigner);

Generating a random, non-deterministic key

The Core SDK also provides a way to generate a random, non-reproducible key using the GenerateKey() method:

🚨🚨🚨 Warning 🚨🚨🚨

If you generate your own Stark private key, you will have to persist it. The key is randomly generated so cannot be deterministically re-generated.

starkPrivateKey, err = stark.GenerateKey(l1signer)
if err != nil {
    log.Panicf("error in Generating Stark Private Key: %v\n", err)
}

Operations requiring user signatures

As XpansionChain enables applications to execute signed transactions on both Ethereum (layer 1) and StarkEx (layer 2), signers are required for both these layers. In order to generate an Ethereum or Stark signer, a user's Ethereum or Stark private key is required.

There are two types of operations requiring user signatures:

  1. Transactions that update blockchain state

  2. Operations that XpansionChain require authorization for

In order to get user signatures, applications need to generate "signers".

What are transactions that update blockchain state?

A common transaction type is the transfer of asset ownership from one user to another (ie. asset sale). These operations require users to sign (approve) them to prove that they are valid.

What are operations that require authorization?

These operations add to or update data in XpansionChain's databases and typically require the authorization of an object's owner (ie. a user creating a project on XpansionChain).

How do applications generate and use signers?

Signers are abstractions of user accounts that can be used to sign transactions. A user's private key is required to generate them.

There are two ways to get signers in your application:

  1. Generate your own by obtaining and using the user's private keys

  2. Use our Wallet SDK to connect to a user's wallet application

The first option, where an application obtains a user's private key directly, is risky because these keys allow anyone in possession of them full control of an account.

The second option provides an application with an interface to the user's account by prompting the user to connect with their wallet application (ie. mobile or browser wallet). Once connected the app can begin asking the user to sign transactions and messages without having to reveal their private key.

1. Generate L1 and L2 signers

The Core SDK provides functionality for applications to generate Stark (L2) signers.

apiConfiguration := api.NewConfiguration()
cfg := imx.Config{
    APIConfig:     apiConfiguration,
    AlchemyAPIKey: YOUR_ALCHEMY_API_KEY,
    Environment:   imx.Sandbox,
}

// Create Ethereum signer
l1signer, err := ethereum.NewSigner(YOUR_PRIVATE_ETH_KEY, cfg.ChainID)
if err != nil {
    log.Panicf("error in creating L1Signer: %v\n", err)
}

// Endpoints like Withdrawal, Orders, Trades, Transfers require an L2 (stark) signer
// Create Stark signer
l2signer, err := stark.NewSigner(YOUR_PRIVATE_STARK_KEY)
if err != nil {
    log.Panicf("error in creating StarkSigner: %v\n", err)
}

2. Generate signers using the Wallet SDK

The Wallet SDK Web provides connections to Metamask and WalletConnect browser wallets.

See this guide for how to set this up.

Examples

Create a project (requires an Ethereum layer 1 signer)

// Create a new project demo.
createProjectResponse, err := c.CreateProject(ctx, l1signer, "My Company", "Project name", "project@company.com")
if err != nil {
    log.Panicf("error in CreateProject: %v\n", err)
}

// Get the project details we just created.
projectId := strconv.FormatInt(int64(createProjectResponse.Id), 10)
getProjectResponse, err := c.GetProject(ctx, l1signer, projectId)
if err != nil {
    log.Panicf("error in GetProject: %v", err)
}

Deposit tokens from L1 to L2 (requires an Ethereum layer 1 signer)

// Eth Deposit
ethAmountInWei := uint(500000000000000000) // Amount in wei
depositResponse, err := imx.NewETHDeposit(ethAmountInWei).Deposit(ctx, c, l1signer, nil)
if err != nil {
    log.Panicf("error calling Eth deposit workflow: %v", err)
}

Create an order (requires an Ethereum layer 1 and StarkEx layer 2 signer)

// The amount (listing price) should be in Wei for Eth tokens,
// see https://docs.starkware.co/starkex-v4/starkex-deep-dive/starkex-specific-concepts
// and https://eth-converter.com/
createOrderRequest := &api.GetSignableOrderRequest{
    AmountBuy:  strconv.FormatUint(amount, 10),
    AmountSell: "1",
    Fees:       nil,
    TokenBuy:   imx.SignableETHToken(),                         // The listed asset can be bought with 
    TokenSell:  imx.SignableERC721Token(tokenID, tokenAddress), // NFT Token
    User:       l1signer.GetAddress(),                          // Address of the user listing for sale.
}
createOrderRequest.SetExpirationTimestamp(0)

// Create order will list the given asset for sale.
createOrderResponse, err := c.CreateOrder(ctx, l1signer, l2signer, createOrderRequest)
if err != nil {
    log.Panicf("error in CreateOrder: %v", err)
}

Contract requests

XpansionChain is built as a zkRollup (zero-knowledge rollup) in partnership with StarkWare. The choice of zkRollups is deliberate, as it offers unparalleled scalability without compromising security. This means the following:

  • Cost-effectiveness: When you mint or trade an NFT on XpansionChain, you enjoy the advantage of zero gas fees.

  • Security: All transactions are secured by zero-knowledge proofs, which makes XpansionChain the first ever layer 2 solution for NFTs on Ethereum.

The Core SDK provides an interface with the smart contracts necessary for interacting with the XpansionChain platform.

See all smart contracts available in the Core SDK.

// This example is only to demonstrate using the generated smart contract clients
// We recommend using the Deposit method from https://github.com/XpansionChain/imx-core-sdk-golang/blob/69af5db9a0be05afd9c91c6b371547cfe3bea719/imx/deposit.go to deposit NFT
func DepositNft(l1signer XpansionChain.L1Signer, starkPublicKey, assetType, vaultID, tokenID *big.Int, overrides *bind.TransactOpts) (*types.Transaction, error) {
    apiConfiguration := api.NewConfiguration()
    cfg := imx.Config{
        APIConfig:     apiConfiguration,
        AlchemyAPIKey: YOUR_ALCHEMY_API_KEY,
        Environment:   imx.Sandbox,
    }
    client, err := imx.NewClient(&cfg)
    if err != nil {
        log.Panicf("error in NewClient: %v\n", err)
    }
    defer c.EthClient.Close()

    opts := c.buildTransactOpts(ctx, l1signer, overrides)
    transaction, err := client.CoreContract.DepositNft(opts, starkPublicKey, assetType, vaultID, tokenID)
    if err != nil {
        return nil, err
    }
    log.Println("transaction hash:", transaction.Hash())
    return transaction, nil
}

Smart contract autogeneration

The XpansionChain Solidity contracts can be found in the contracts folder. Contract bindings in Golang are generated using abigen.

Contract
Description

Core

The Core contract is XpansionChain's main interface with the Ethereum blockchain, based on StarkEx.

Registration

The Registration contract is a proxy smart contract for the Core contract that combines transactions related to onchain registration, deposits, and withdrawals. When a user who is not registered onchain attempts to perform a deposit or a withdrawal, the Registration combines requests to the Core contract in order to register the user first. Users who are not registered onchain are not able to perform a deposit or withdrawal.

IERC20

Standard interface for interacting with ERC20 contracts, taken from OpenZeppelin.

IERC721

Standard interface for interacting with ERC721 contracts, taken from OpenZeppelin.

Further documentation

  • See the Developer homepage for general information on building on XpansionChain.

  • Build on XpansionChain zkEVM:

    • Documentation

    • API reference

    • Support

  • Build on XpansionChain:

    • Documentation

    • API reference

    • Support

Last updated