🚧 Bloque documentation is under development

Swap

Learn how to perform asset swaps using the Bloque SDK.

Overview

The swap module lets you query exchange rates and perform asset swaps between supported mediums and currencies.

Querying Exchange Rates

import { SDK } from '@bloque/sdk';

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'production',
});

const userSession = await bloque.connect('user-alias');

const result = await userSession.swap.findRates({
  fromAsset: 'COP/2',
  toAsset: 'DUSD/6',
  fromMediums: ['bancolombia', 'pse'], // required
  toMediums: ['kusama'], // required
  amountSrc: '50000000', // 500000.00 COP (scaled by 2 decimals)
  sort: 'asc', // optional, default: 'asc'
  sortBy: 'rate', // optional, default: 'rate'
});
console.log('Swap rates result:', result);

Parameters

FindRatesParams

FieldTypeRequiredDescription
fromAssetstringYesSource asset with precision (e.g., "COP/2")
toAssetstringYesDestination asset with precision (e.g., "DUSD/6")
fromMediumsstring[]YesSource payment mediums (e.g., ["bancolombia"])
toMediumsstring[]YesDestination payment mediums (e.g., ["kusama"])
amountSrcstringOptionalSource amount as bigint string (scaled by asset precision)
amountDststringOptionalDestination amount as bigint string (scaled by asset precision)
sort`'asc''desc'`Optional
sortBy`'rate''at'`Optional

Response

FindRatesResult

interface FindRatesResult {
  rates: SwapRate[];
}

SwapRate

interface SwapRate {
  id: string;
  sig: string;
  swapSig: string;
  maker: string;
  edge: [string, string];
  fee: Fee;
  at: string;
  until: string;
  fromMediums: string[];
  toMediums: string[];
  rate: [number, number];
  ratio: number;
  fromLimits: [string, string];
  toLimits: [string, string];
  createdAt: string;
  updatedAt: string;
}

Fee

interface Fee {
  at: number;
  value: number;
  formula: string;
  components: FeeComponent[];
}

FeeComponent

interface FeeComponent {
  at: number;
  name: string;
  type: 'percentage' | 'rate' | 'fixed';
  value: number | string;
  percentage?: number;
  pair?: string;
  amount?: number;
}

Complete Example Flow

import { SwapClient } from '@bloque/swap';

const client = new SwapClient({ /* config */ });

async function queryRates() {
  const result = await client.findRates({
    fromAsset: 'COP/2',
    toAsset: 'DUSD/6',
    fromMediums: ['bancolombia'],
    toMediums: ['kusama'],
    amountSrc: '50000000',
  });

  if (result.rates.length > 0) {
    const bestRate = result.rates[0];
    console.log('Ratio:', bestRate.ratio);
    console.log('Fee:', bestRate.fee.value);
  } else {
    console.log('No rates available');
  }
}

queryRates();

Best Practices

  1. Validate input parameters before querying rates.
  2. Use the fromLimits and toLimits fields to check allowed amounts.
  3. Handle possible network or API errors gracefully.
  4. Query rates right before performing a swap to avoid expiration.
  5. Check the until field to know how long a rate is valid.

PSE-Specific Best Practices

Required Customer Data

For successful PSE transactions, ensure you provide the following customer data:

  • bank_code: Valid bank code from pse.banks() list
  • user_type: 0 for natural persons, 1 for legal entities
  • customer_email: Valid email address for transaction notifications
  • user_legal_id_type: Document type ('CC' for citizenship card, 'NIT' for tax ID, 'CE' for foreign ID)
  • user_legal_id: Valid ID number matching the specified type
  • customer_data.full_name: Customer's complete legal name

Common Validation Requirements

  • Ensure the legal ID number matches the selected ID type
  • Use the exact bank code from the banks list (no modifications)
  • Provide a valid email format for customer notifications
  • Full name should match the customer's legal documentation

Error Handling

try {
  const result = await userSession.swap.pse.create({
    // ... parameters
  });
} catch (error) {
  if (error instanceof BloqueValidationError) {
    // Handle validation errors (missing fields, invalid IDs, etc.)
    console.error('Validation failed:', error.message);
  } else if (error instanceof BloqueAPIError) {
    // Handle API errors (bank unavailable, rate expired, etc.)
    console.error('API error:', error.message);
  }
}

Next Steps

Listing PSE Banks

You can retrieve the list of available PSE (Pagos Seguros en Línea) banks and their codes for PSE payments:

const pseBanks = await userSession.swap.pse.banks();
for (const bank of pseBanks.banks) {
  console.log(`${bank.code}: ${bank.name}`);
}

Bank Type

interface Bank {
  code: string; // Bank code for PSE
  name: string; // Bank name
}

This is useful for presenting a list of banks to users when initiating PSE-based swaps or payments.

Create PSE Swap Order

The SDK allows you to create swap orders using PSE (Pagos Seguros en Línea) as the source payment medium. The pse.create method combines order creation and optionally auto-executes the first instruction node to initiate the payment flow.

Basic Usage

// 1. Find available rates
const rates = await userSession.swap.findRates({
  fromAsset: 'COP/2',
  toAsset: 'DUSD/6',
  fromMediums: ['pse'],
  toMediums: ['kreivo'],
  amountSrc: '1000000', // 10,000.00 COP
});

// 2. Create PSE swap order
const result = await userSession.swap.pse.create({
  rateSig: rates.rates[0].sig,
  toMedium: 'kreivo',
  amountSrc: '1000000',
  depositInformation: {
    urn: 'did:bloque:account:card:usr-xxx:crd-xxx'
  },
  args: {
    bankCode: '1007',
    userType: 0,
    customerEmail: 'user@example.com',
    userLegalIdType: 'CC',
    userLegalId: '123456789',
    customerData: {
      fullName: 'User Name',
      phoneNumber: '3018362958',
    }
  }, // Auto-executes PSE payment
});

// 3. Redirect user to PSE
if (result.execution?.result.how?.url) {
  window.location.href = result.execution.result.how.url;
}

CreatePseOrderParams Parameters

FieldTypeRequiredDescription
rateSigstringYesRate signature obtained from findRates
toMediumstringYesDestination medium (e.g., 'kreivo', 'bloque')
amountSrcstringConditionalSource amount as bigint string (required if type is 'src')
amountDststringConditionalDestination amount as bigint string (required if type is 'dst')
type'src' | 'dst'NoOrder type (default: 'src')
depositInformationDepositInformationNoDeposit information for fund delivery
argsPsePaymentArgsYesPSE arguments for auto-execution
nodeIdstringNoSpecific node ID to execute
metadataRecord<string, unknown>NoAdditional metadata for the order

DepositInformation Type

interface DepositInformation {
  urn: string; // Account URN where funds will be deposited
  // Example: "did:bloque:account:card:usr-xxx:crd-xxx"
}

PsePaymentArgs Type

interface PsePaymentArgs {
  bankCode: string;           // Bank code (from pse.banks())
  userType: 0 | 1;          // 0 for natural person, 1 for legal entity
  customerEmail: string;    // Customer email address
  userLegalIdType: 'CC' | 'NIT' | 'CE'; // User legal ID type (e.g., 'CC', 'NIT', 'CE')
  userLegalId: string;     // User legal ID number
  customerData: {           // Additional customer data
    fullName: string;         // Customer's full name
    phoneNumber: string;
  };
}

CreatePseOrderResult Response

interface CreatePseOrderResult {
  order: SwapOrder;           // Created order details
  execution?: ExecutionResult; // Auto-execution result (if args were provided)
  requestId: string;          // Request ID for tracking
}

SwapOrder Type

interface SwapOrder {
  id: string;                        // Unique order ID
  orderSig: string;                  // Order signature
  rateSig: string;                   // Rate signature used
  swapSig: string;                   // Swap signature
  taker: string;                     // Taker URN
  maker: string;                     // Maker URN
  fromAsset: string;                 // Source asset
  toAsset: string;                   // Destination asset
  fromMedium: string;                // Source medium
  toMedium: string;                  // Destination medium
  fromAmount: string;                // Source amount
  toAmount: string;                  // Destination amount
  depositInformation: DepositInformation; // Deposit information
  at: number;                        // Creation timestamp
  graphId: string;                   // Instruction graph ID
  status: SwapOrderStatus;           // Order status
  createdAt: string;                 // Creation date
  updatedAt: string;                 // Last update date
}

Swap Order States

type SwapOrderStatus =
  | 'pending'      // Order created, waiting for execution
  | 'in_progress'  // Order in process of execution
  | 'completed'    // Order completed successfully
  | 'failed'       // Order failed
  | 'cancelled'    // Order cancelled
  | 'expired';     // Order expired

ExecutionResult Type

interface ExecutionResult {
  nodeId: string;   // Executed node ID
  result: {
    status: ExecutionStatus;  // Execution status
    args?: unknown[];     // Additional arguments
    description?: string; // Result description
    how?: ExecutionHow;   // Instructions for completing this step
  };
}

Execution States

type ExecutionStatus =
  | 'pending'    // Node pending execution
  | 'running'   // Node executing
  | 'completed'  // Node completed successfully
  | 'failed'     // Node failed during execution
  | 'timeout';   // Node timed out

States and Transitions

Order States

StateDescriptionCan Transition To
pendingOrder created, waiting for executionin_progress, cancelled, expired
in_progressOrder in process of executioncompleted, failed
completedOrder completed successfully-
failedOrder failed-
cancelledOrder cancelled manually-
expiredOrder expired by time-
stateDiagram-v2
    [*] --> pending
    pending --> in_progress
    pending --> cancelled
    pending --> expired
    in_progress --> completed
    in_progress --> failed
    completed --> [*]
    failed --> [*]
    cancelled --> [*]
    expired --> [*]

Node Execution States

StateDescriptionCan Transition To
pendingNode pending executionrunning, timeout
runningNode actively executingcompleted, failed, timeout
completedNode completed successfully-
failedNode failed during execution-
timeoutNode timed out-
stateDiagram-v2
    [*] --> pending
    pending --> running
    pending --> timeout
    running --> completed
    running --> failed
    running --> timeout
    completed --> [*]
    failed --> [*]
    timeout --> [*]

Complete PSE Flow Example

import { SDK } from '@bloque/sdk';

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'production',
});

async function performPSESwap() {
  const userSession = await bloque.connect('user-alias');

  // 1. Get list of banks
  const { banks } = await userSession.swap.pse.banks();
  console.log('Available banks:', banks);

  // 2. Find available rates
  const rates = await userSession.swap.findRates({
    fromAsset: 'COP/2',
    toAsset: 'DUSD/6',
    fromMediums: ['pse'],
    toMediums: ['kreivo'],
    amountSrc: '50000000', // 500,000.00 COP
  });

  if (rates.rates.length === 0) {
    console.log('No rates available');
    return;
  }

  // 3. Create PSE swap order with auto-execution
  const result = await userSession.swap.pse.create({
    rateSig: rates.rates[0].sig,
    toMedium: 'kreivo',
    amountSrc: '50000000',
    depositInformation: {
      urn: 'did:bloque:account:card:usr-2p9pn77R6mWHqH96KYKQBylasRD:crd-38uPtyeKTjalMK1jysgoAql3l9n',
    },
    args: {
      bankCode: banks[0].code, // Bank selected by user
      userType: 0, // Natural person
      customerEmail: 'user@example.com',
      userLegalIdType: 'CC',
      userLegalId: '123456789',
      customerData: {
        fullName: 'John Doe',
        phoneNumber: '3018362958',
      },
    },
  });

  console.log('Order created:', result.order.id);
  console.log('Status:', result.order.status);
  console.log('Graph ID:', result.order.graphId);

  // 4. Redirect user to PSE if redirect URL is available
  if (result.execution?.result.how?.url) {
    console.log('Redirecting to PSE...');
    window.location.href = result.execution.result.how.url;
  }
}

performPSESwap();

Create Bancolombia Swap Order

The SDK allows you to create swap orders using Kusama as the source payment medium and Bancolombia as the destination. The bancolombia.create method combines order creation and optionally auto-executes the first instruction node to initiate the swap flow.

Basic Usage

// 1. Find available rates
const rates = await userSession.swap.findRates({
  fromAsset: 'COPM/2',
  toAsset: 'COP/2',
  fromMediums: ['kusama'],
  toMediums: ['bancolombia'],
  amountSrc: '1000000', // 10,000.00 COP (2 decimals)
});

// 2. Create Bancolombia swap order
const result = await userSession.swap.bancolombia.create({
  rateSig: rates.rates[0].sig,
  amountSrc: '1000000',
  depositInformation: {
    bankAccountType: 'savings',
    bankAccountNumber: '5740088718',
    bankAccountHolderName: 'jon Doe',
    bankAccountHolderIdentificationType: 'CC',
    bankAccountHolderIdentificationValue: '123456789'
  },
  args: {
    accountUrn: 'did:bloque:card:1231231'
  }
});

// 3. Check order status
console.log('Order created:', result.order.id);
console.log('Status:', result.order.status);

CreateBancolombiaOrderParams Parameters

FieldTypeRequiredDescription
rateSigstringYesRate signature obtained from findRates
amountSrcstringConditionalSource amount as bigint string (required if type is 'src')
amountDststringConditionalDestination amount as bigint string (required if type is 'dst')
type'src' | 'dst'NoOrder type (default: 'src')
depositInformationBancolombiaDepositInformationYesBancolombia bank account information for fund delivery
argsKusamaAccountArgsNoKusama account arguments for auto-execution
nodeIdstringNoSpecific node ID to execute
metadataRecord<string, unknown>NoAdditional metadata for the order

BancolombiaDepositInformation Type

interface BancolombiaDepositInformation {
  bankAccountType: 'savings' | 'checking';                    // Account type
  bankAccountNumber: string;                                   // Account number
  bankAccountHolderName: string;                                // Account holder name
  bankAccountHolderIdentificationType: 'CC' | 'CE' | 'NIT' | 'PP'; // ID type
  bankAccountHolderIdentificationValue: string;                // ID number
}

KusamaAccountArgs Type

interface KusamaAccountArgs {
  accountUrn: string;  // Kusama account URN for source funds
}

Complete Bancolombia Flow Example

import { SDK } from '@bloque/sdk';

const bloque = new SDK({
  origin: 'your-origin',
  auth: {
    type: 'apiKey',
    apiKey: process.env.BLOQUE_API_KEY!,
  },
  mode: 'production',
});

async function performBancolombiaSwap() {
  const userSession = await bloque.connect('user-alias');

  // 1. Find available rates for Kusama to Bancolombia
  const rates = await userSession.swap.findRates({
    fromAsset: 'COPM/2',
    toAsset: 'COP/2',
    fromMediums: ['kusama'],
    toMediums: ['bancolombia'],
    amountSrc: '1000000', // 10,000.00 COP
  });

  if (rates.rates.length === 0) {
    console.log('No rates available');
    return;
  }

  // 2. Create Bancolombia swap order with bank details
  const result = await userSession.swap.bancolombia.create({
    rateSig: rates.rates[0].sig,
    amountSrc: '1000000',
    type: 'src', // Specify exact KSM amount to pay
    depositInformation: {
      bankAccountType: 'savings',
      bankAccountNumber: '5740088718',
      bankAccountHolderName: 'jon Doe',
      bankAccountHolderIdentificationType: 'CC',
      bankAccountHolderIdentificationValue: '123456789'
    },
    args: {
      accountUrn: 'did:bloque:card:1231231' // Source Kusama account
    }
  });

  console.log('Order created:', result.order.id);
  console.log('Status:', result.order.status);
  console.log('From Amount:', result.order.fromAmount);
  console.log('To Amount:', result.order.toAmount);
  console.log('Graph ID:', result.order.graphId);

  // 3. Monitor order execution (if applicable)
  if (result.execution) {
    console.log('Execution started:', result.execution.nodeId);
  }
}

performBancolombiaSwap();

Order Types

  • src (default): User specifies the exact amount to pay. Destination amount is calculated from the rate.

    • Example: "I want to pay exactly 10,000 COP, give me whatever DUSD that gets me"
  • dst: User specifies the exact amount to receive. Source amount is calculated from the rate.

    • Example: "I want to receive exactly 5 DUSD, I'll pay whatever COP is needed"
  • Accounts Guide - Create and manage accounts for swap operations

  • Organizations Guide - Manage entities that can perform swaps