Transaction Requests API

TransactionRequests are off-chain requests for all sorts of blockchain transactions before they are sent to the blockchain.

Typically, a transaction request is created when a user wants to perform a transaction on the blockchain. The transaction request will then be validated against the wallet's existing policies, and then sent to the wallet's approval quorum for approval. Once the transaction request is approved, the transaction needs to be signed by the wallet's signer before it is sent to the blockchain.

Refer to the Transaction Request Workflow for more information on the transaction request lifecycle.

Create

Native Token Transfer

To perform native crypto transfers, you can use the SDK or the GraphQL mutation as follows.

You can toggle between TypeScript and GraphQL to see the code examples.

SDKTypeScript
import { LevainGraphClient } from '@levain/wallet-sdk';

const client = new LevainGraphClient({
  // Get your personal access token from https://app.levain.tech/account/tokens
  accessToken: '<Your access token>',
});

const walletId = '8b7a9f2d-194b-4e9b-a0f5-d500b9a0201a';

// initiate a "send" transaction of 0.001 ETH to 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
const { requestId } = await client.sendAsset({
  walletId,
  to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  amount: '0.001',

  /*
   The asset identifier is a CAIP-19 asset identifier, which is a standardized format for identifying assets across
   different blockchains.
    - Ethereum mainnet eth: caip19:eip155:1/slip44:60
    - Ethereum mainnet weth: caip19:eip155:1/erc20:0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
    - Bitcoin mainnet UTXO: caip19:bip122:000000000019d6689c085ae165831e93/slip44:0
    - Bitcoin testnet UTXO: caip19:bip122:000000000933ea01ad0ee984209779ba/slip44:1
   For more examples, see https://developer.levain.tech/products/graph/docs/levain-fundamentals/supported-tokens 
  */
  asset: 'caip19:eip155:1/slip44:60',
});

// explicit approval
await client.approveTransactionRequest({ requestId });

// create the digests to be signed client-side
const { digests } = await client.createTransactionDigests({ requestId });

// client-side signing is performed on the digests, and the signatures are sent to Levain
// the transaction is then broadcasted to the blockchain network
const executedTransaction = await client.executeTransaction({
  walletId,
  requestId,
  digests,
  walletPassword: '...',
});

The walletId is provided by the createWallet mutation (i.e. at wallet creation time).

ERC-20 Token Transfer

To perform ERC-20 token transfers on EVM-compatible blockchains, you can use the SDK or the GraphQL mutation as follows.

SDKTypeScript
import { LevainGraphClient } from '@levain/wallet-sdk';

const client = new LevainGraphClient({
  // Get your personal access token from https://app.levain.tech/account/tokens
  accessToken: '<Your access token>',
});

const walletId = '8b7a9f2d-194b-4e9b-a0f5-d500b9a0201a';

const { requestId } = await client.sendAsset({
  walletId,
  // Sepolia wETH
  // https://sepolia.etherscan.io/address/0x7b79995e5f793a07bc00c21412e50ecae098e7f9
  asset: 'caip19:eip155:11155111/erc20:0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9',
  to: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045',
  amount: '0.001',
  clientRequestId: '...', // Optional, you can pass in a unique reference ID for the transaction for idempotency
});

// explicit approval
await client.approveTransactionRequest({ requestId });

// create the digests to be signed client-side
const { digests } = await client.createTransactionDigests({ requestId });

// client-side signing is performed on the digests, and the signatures are sent to Levain
// the transaction is then broadcasted to the blockchain network
const executedTransaction = await client.executeTransaction({
  walletId,
  requestId,
  digests,
  walletPassword: '...',
});

TRC-20 Token Transfers

Largely similar to ERC-20 token transfers, you can use the SDK or the GraphQL mutation as follows.

SDKTypeScript
import { LevainGraphClient } from '@levain/wallet-sdk';

const client = new LevainGraphClient({
  // Get your personal access token from https://app.levain.tech/account/tokens
  accessToken: '<Your access token>',
});

const walletId = '8b7a9f2d-194b-4e9b-a0f5-d500b9a0201a';

const { requestId } = await client.sendAsset({
  walletId,
  // Tron USDT - https://tronscan.org/#/token20/TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
  asset: 'caip19:tip474:728126428/trc20:TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
  to: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N',
  amount: '0.001',
});

// explicit approval
await client.approveTransactionRequest({ requestId });

// create the digests to be signed client-side
const { digests } = await client.createTransactionDigests({ requestId });

// client-side signing is performed on the digests, and the signatures are sent to Levain
// the transaction is then broadcasted to the blockchain network
const executedTransaction = await client.executeTransaction({
  walletId,
  requestId,
  digests,
  walletPassword: '...',
});

Retrieve

By Transaction Request ID

You can retrieve a transaction request using the following GraphQL query with the TransactionRequest object.

QueryGraphQL
query TransactionRequestById {
  wallet(walletId: "d628e653-becd-412d-bdef-dbab620631d9") {
    transactionRequest(requestId: "f33e01d2-25e9-4f63-9e61-788ae20df7c3") {
      status
      transactionHash
      transactionSignature
      transactionSigned
      asset {
        name
        decimals
        isNative
      }
      initiator {
        user {
          email
          firstName
          lastName
        }
      }
      approvals {
        edges {
          node {
            actor {
              user {
                email
                firstName
                lastName
              }
            }
          }
        }
      }
    }
  }
}
ResponseJSON
{
  "data": {
    "wallet": {
      "transactionRequest": {
        "status": "SIGNED",
        "transactionHash": "0x5c386f2c2cdc9d55d73ce96b9a5b929ea1693cddad4b5500b13a214a9c9b2b52",
        "transactionSignature": "0xad56808e1dae318db5e48c9efc85b913cb29ad8f1d9d5b3e32a5228d1df314c35f579924e8c5330ff531728121eadfc6cda5999f0e7aa77520886b8cdce25e531b5c42986377c38755633a7c2e61ae2363f5f9d72ae8e2ec931ed9c82dbfb6b9bb092421103538bb7d506d47c82b3cfe4113976612779c8b2257ae90ac39ddbcb91b",
        "transactionSigned": "0x02f9021383aa36a7038459682f008459682f8e82f75d946886c0a002c3de06481330adb9e25e76d5bf2d2e80b901a460b4cda700000000000000000000000000000000000000000000000000000000000000c00000000000000000000000008d080e0d0ff69f5801dd3c4f628fb72f595a1bd00000000000000000000000000000000000000000000000000000886c98b760000000000000000000000000000000000000000000000000000000000000000180000000000000000000000000a060029846886406d05bb5df82d417c4200ac90000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000082ad56808e1dae318db5e48c9efc85b913cb29ad8f1d9d5b3e32a5228d1df314c35f579924e8c5330ff531728121eadfc6cda5999f0e7aa77520886b8cdce25e531b5c42986377c38755633a7c2e61ae2363f5f9d72ae8e2ec931ed9c82dbfb6b9bb092421103538bb7d506d47c82b3cfe4113976612779c8b2257ae90ac39ddbcb91b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c001a00c0d39743ee31ea6c9a1a792966dcbf263732ba563de891d22b3f6cf57e9492ea003dca15020c2337e55c4b3ead1ba91f867af7cc20fe0060a49e312d37db68ade",
        "asset": {
          "name": "Sepolia Uniswap",
          "decimals": 18,
          "isNative": false
        },
        "initiator": {
          "user": {
            "email": "[email protected]",
            "firstName": "API Initiator",
            "lastName": "Service Account"
          }
        },
        "approvals": {
          "edges": [
            {
              "node": {
                "actor": {
                  "user": {
                    "email": "[email protected]",
                    "firstName": "API Approver",
                    "lastName": "Service Account"
                  }
                }
              }
            }
          ]
        }
      }
    }
  }
}