Generate signers

Generate signers

User signatures are required for certain types of operations on XpansionChain. These are:

  1. Transactions that update blockchain state

  2. Operations that update XpansionChain's databases that require user authorization for

Examples:

✅ Require user signatures
❌ Do not require user signatures

Transactions that update blockchain state

Operations requiring user authorization

Read-only operations

  • Creating an order

  • Filling an order (creating a trade)

  • Transferring assets between users

  • Depositing assets on L2 (XpansionChain)

  • Withdrawing assets to L1 (Layer 1 Ethereum)

  • Creating a project

  • Registering a user off-chain

  • Getting a list of all assets on XpansionChain

  • Getting the details (ie. metadata) of a particular asset

  • Getting a list of all open orders

  • Getting a list of historical trades

Using "signers" to get user signatures

In order to get user signatures, applications can use "signers". These are abstractions of user accounts that can be used to sign transactions. A user's private key is required to generate them.

Ethereum (L1) signers are required to sign transactions on L1 (ie. depositing assets from the L1 wallet to the L2 one) and Stark (L2) signers are required to sign transactions on L2 (ie. creating an order on an L2 marketplace, transferring an asset to another user on L2).

How do applications generate and use signers?

There are two ways to get signers in your application:

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

  2. 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 your own signers

💡AVAILABLE WITH:

  • Core SDK

Core SDK

Below are instructions on how to generate:

  • Ethereum (L1) signers

  • Stark (L2) private keys and signers using the Core SDK

CAUTION

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.

  • Typescript Core SDK

The Core SDK provides functionality for applications to generate Stark (L2) private keys and signers:

import { AlchemyProvider } from '@ethersproject/providers';
import { Wallet } from '@ethersproject/wallet';
import { generateStarkPrivateKey, createStarkSigner } 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);

// Create Stark signer
const starkPrivateKey = generateStarkPrivateKey(); // Or retrieve previously generated key
const starkSigner = createStarkSigner(starkPrivateKey);
  • Kotlin (JVM) Core SDK

In order to use any workflow functions, you will need to pass in the connected wallet provider. This means you will need to implement your own Wallet L1 Signer.

Once you have created a Signer instance you can generate the user's Stark key pair and use it to create an instance of StandardStarkSigner, an implementation of StarkSigner.

StarkKey.generate(signer).whenComplete { keyPair, error ->
    val starkSigner = StandardStarkSigner(keyPair)
}
  • Swift Core SDK

In order to use any workflow functions, you will need to pass in the connected wallet provider. This means you will need to implement your own Wallet L1 Signer and L2 StarkSigner.

Once you have a Signer instance you can generate the user's Stark key pair and use the result to instantiate a StarkSigner, for example, by using the default StandardStarkSigner provided by the SDK.

let keyPair = try await StarkKey.generateKeyPair(from: signer)
let starkSigner = StandardStarkSigner(pair: keyPair)
  • Golang Core SDK

1. Generate L1 signer

When you implement an L1signer, it must satisfy L1Signer interface. See BaseL1Signer for a sample implementation of an L1 Signer.

Also refer to examples/publicapi/list_assets/main.go for environment setup examples.

2. Generate L2 signer

See signers/stark for information about generating your own L2 signer, as well as the following code snippet:

import (
   "github.com/XpansionChain/imx-core-sdk-golang/signers/stark"
   ...
)

func main() {
   // L1 credentials
   l1signer := YourImplementationOfL1SignerInterface() // See examples/workflows/utils/signer.go

   // L2 credentials
   // Obtain the stark signer associated with this user.
   l2signer, err := stark.GenerateStarkSigner(l1signer) // this is the sdk helper function
   if err != nil {
      ...
   }
}

2. Connect to users' wallets

Your application can facilitate signing of user transactions by connecting to users' wallet applications. This ensures that you do not have to handle private keys.

💡AVAILABLE WITH:

  • Wallet SDK (with Core SDK)

  • Link SDK

  • Wallet SDK

The Wallet SDK provides a way for applications to connect to certain user wallets:

Wallet SDK
Platform
Wallets supported

Wallet SDK Web

Web

  • Metamask

  • WalletConnect

Wallet SDK Android

Android

  • Any wallet that supports WalletConnect v1.0

Wallet SDK iOS

iOS

  • Any wallet that supports WalletConnect v1.0

  • Wallet SDK Web

  1. Install the Wallet SDK npm package:

npm install @imtbl/wallet-sdk-web --save
  1. Set up the Wallet SDK and use with the Core SDK for Typescript:

import { ENVIRONMENTS, L1_PROVIDERS, WalletSDK } from '@imtbl/wallet-sdk-web';

(async () => {
  // Builds the Wallet SDK object
  const sdk = await WalletSDK.build({
    env: ENVIRONMENTS.STAGING,
    /*
      RPC config is only required if the WalletConnect provider (L1_PROVIDERS.WALLET_CONNECT)
      is being used. Follow this reference for the RPC config:
      https://docs.walletconnect.com/quick-start/dapps/web3-provider#provider-options
    */
    rpc: {
      5: 'https://goerli.mycustomnode.com',
    },
    /*
      Will switch the chain based on this configured chainID when connecting to the wallet.(Optional)
      Following the table below to get the chainID and name mapping. 
      Consult https://chainlist.org/ for more.
      ChainId   | Network
      --- --- | --- --- 
      1       | Ethereum Main Network (Mainnet)
      5       | Goerli Test Network
    */
    chainID: 5,
  });

  // Connects on the chosen provider - WalletConnect
  const walletConnection = await sdk.connect({
    provider: L1_PROVIDERS.WALLET_CONNECT,
  });
  // For Metamask:
  // const walletConnection = await sdk.connect({ provider: L1_PROVIDERS.METAMASK });

  // Use with the Core SDK for Typescript, e.g. Register a user
  await client.registerOffchain(walletConnection);
})();

TIP

The WalletConnection object can also be retrieved in the following ways:

// By calling the method `getWalletConnection`
const walletConnection = sdk.getWalletConnection();

// By listening to the event `WALLET_SDK_EVENTS.CONNECTION_UPDATED`
walletSdkEvents.on(
  WALLET_SDK_EVENTS.CONNECTION_UPDATED,
  (updatedWalletConnection: WalletConnection) => {
    const walletConnection = updatedWalletConnection;
  }
);
  • Wallet SDK Android

  1. Add Maven Central and JitPack to your repositories

repositories {
    mavenCentral()
    maven { url = "https://jitpack.io" } // Needed for WalletConnect
}
  1. Add the dependency to your app's build.gradle file:

dependencies {
    implementation 'com.XpansionChain.wallet:imx-wallet-sdk-android:$version'
}
  1. In your Application class:

class ExampleApplication : Application() {
  override fun onCreate() {
    super.onCreate()
   XpansionChainXWallet.init(this)
  }
}
  1. Connect to the user's wallet

XpansionChainXWallet.connect(
    Provider.WalletConnect(
        appUrl = "https://www.marketplace.com/",
        appName = "My NFT Marketplace",
        appDescription = "This is a marketplace where all my favorite NFTs can be traded.",
        appIcons = listOf("http://www.marketplace.com/appicon.svg")
    )
)

If you want to use your own bridge server instead of the default provide it via bridgeServerUrl when connecting. For more info on how WalletConnect and the bridge works see here.

  1. Use with the Core SDK for Kotlin/JVM

Once you connect a user's wallet with the Wallet SDK you can provide the Signer and StarkSigner instances to Core SDK workflows:

val signer = XpansionChainXWallet.signer
val starkSigner = XpansionChainXWallet.starkSigner
if (signer != null && starkSigner != null) {
    XpansionChainXCore.buy(orderId, emptyList(), signer, starkSigner).whenComplete { ... }
} else {
    // Handle not connected
}
  • Wallet SDK iOS

  1. Add pod XpansionChainXWallet to your Podfile

platform :ios, '13.0'
use_frameworks!

target 'MyApp' do
  pod 'XpansionChainXWallet'
end
  1. Connect to the user's wallet

try await XpansionChainXWallet.shared.connect(
    to: .walletConnect(
        config: .init(
            appURL: URL(string: "https://XpansionChain.com")!,
            appName: "XpansionChain Sample",
            // The Universal Link or URL Scheme of the chosen wallet to be connected.
            walletDeeplink: "https://metamask.app.link"
        )
    )
)

If you want to use your own bridge server instead of the default provide it via bridgeServerUrl when connecting. For more info on how WalletConnect and the bridge works see here.

  1. Use with the Core SDK for Swift

Once you connect a user's wallet with the Wallet SDK you can provide the Signer and StarkSigner instances to Core SDK workflows:

guard let signer = XpansionChainWallet.shared.signer,
    let starkSigner = XpansionChainWallet.shared.starkSigner else {
    // handle not connected
    return
}

let result = try await XpansionChainCore.shared.buy(
    orderId: orderId,
    signer: signer,
    starkSigner: starkSigner
)
  • Link SDK

  1. Install the npm package:

npm install @imtbl/imx-sdk --save
# or
yarn add @imtbl/imx-sdk
  1. Import the Link package:

import { Link } from '@imtbl/imx-sdk';
  1. Set the correct network URL

Choose from the following:

Network
Description
URL

Sandbox

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

https://link.sandbox.x.XpansionChain.com/v1

Production

Ethereum network

https://link.x.XpansionChain.com/v1

const linkAddress = 'https://link.x.XpansionChain.com/v1'; // Or "https://link.sandbox.x.immutable.com/v1"
  1. Initialize the client

const link = new Link(linkAddress);

Operations like registering a user (see guide) can be executed by the Link client, which uses the JS SDK under the hood.

Last updated