Swap
OUTSIDE BLOQUE
INSIDE BLOQUE
PSEPSE Banks
COP via bank auth
BRBBRE-B Keys
instant payments
SWAP
exchange + rates
→cash-in
←cash-out
├BCOBancolombia
Colombian bank
—→ cash-in: external money enters Bloque (e.g. PSE → Card)
—← cash-out: Bloque balance exits to external destination
El módulo swap es la frontera entre los rieles de pago externos y las cuentas Bloque. Úsalo para consultar tasas de cambio y crear órdenes de cash-in o cash-out.
Medios Soportados
Usa fromMediums para especificar cómo entran los fondos al swap, y toMediums para indicar dónde llegan.
Descripción General
El módulo swap permite consultar tasas de cambio y realizar intercambios de activos entre diferentes medios y monedas soportadas.
Realizando una Consulta de Tasas
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);
Parámetros
FindRatesParams
Respuesta
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;
}
Ejemplo Completo de Flujo
import { SwapClient } from '@bloque/swap';
const client = new SwapClient({ /* configuración */ });
async function consultarTasas() {
const resultado = await client.findRates({
fromAsset: 'COP/2',
toAsset: 'DUSD/6',
fromMediums: ['bancolombia'],
toMediums: ['kusama'],
amountSrc: '50000000',
});
if (resultado.rates.length > 0) {
const mejorTasa = resultado.rates[0];
console.log('Ratio:', mejorTasa.ratio);
console.log('Fee:', mejorTasa.fee.value);
} else {
console.log('No se encontraron tasas disponibles');
}
}
consultarTasas();
Mejores Prácticas
- Valida los parámetros de entrada antes de consultar tasas.
- Usa los campos de límites (
fromLimits, toLimits) para validar montos permitidos.
- Maneja correctamente los posibles errores de red o de la API.
- Consulta tasas justo antes de realizar una operación para evitar expiraciones.
- Revisa el campo
until para saber hasta cuándo es válida la tasa.
Próximos Pasos
Listar Bancos PSE
Puedes obtener la lista de bancos disponibles para pagos PSE (Pagos Seguros en Línea) junto con sus códigos:
const pseBanks = await userSession.swap.pse.banks();
for (const bank of pseBanks.banks) {
console.log(`${bank.code}: ${bank.name}`);
}
Tipo Bank
interface Bank {
code: string; // Código del banco para PSE
name: string; // Nombre del banco
}
Esto es útil para mostrar la lista de bancos al usuario al iniciar swaps o pagos vía PSE.
Crear Orden de Swap con PSE
El SDK permite crear órdenes de swap usando PSE (Pagos Seguros en Línea) como medio de pago origen. El método pse.create combina la creación de la orden y opcionalmente auto-ejecuta el primer nodo de instrucciones para iniciar el flujo de pago.
Uso Básico
// 1. Buscar tasas disponibles
const rates = await userSession.swap.findRates({
fromAsset: 'COP/2',
toAsset: 'DUSD/6',
fromMediums: ['pse'],
toMediums: ['kreivo'],
amountSrc: '1000000', // 10,000.00 COP
});
// 2. Crear orden de swap con PSE
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-ejecuta el pago PSE
});
// 3. Redirigir al usuario a PSE
if (result.execution?.result.how?.url) {
window.location.href = result.execution.result.how.url;
}
Parámetros CreatePseOrderParams
interface DepositInformation {
urn: string; // URN de la cuenta donde se depositarán los fondos
// Ejemplo: "did:bloque:account:card:usr-xxx:crd-xxx"
}
Tipo PsePaymentArgs
interface PsePaymentArgs {
bankCode: string; // Código del banco (de pse.banks())
userType: 0 | 1; // 0 para persona natural, 1 para persona jurídica
customerEmail: string; // Correo electrónico del cliente
userLegalIdType: 'CC' | 'NIT' | 'CE'; // Tipo de identificación legal (ej: 'CC', 'NIT', 'CE')
userLegalId: string; // Número de identificación legal
customerData: { // Datos adicionales del cliente
fullName: string; // Nombre completo del cliente,
phoneNumber: string;
};
}
Respuesta CreatePseOrderResult
interface CreatePseOrderResult {
order: SwapOrder; // Detalles de la orden creada
execution?: ExecutionResult; // Resultado de auto-ejecución (si se pasaron args)
requestId: string; // ID de la solicitud para tracking
}
Tipo SwapOrder
interface SwapOrder {
id: string; // ID único de la orden
orderSig: string; // Firma de la orden
rateSig: string; // Firma del rate usado
swapSig: string; // Firma del swap
taker: string; // URN del tomador
maker: string; // URN del maker
fromAsset: string; // Activo origen
toAsset: string; // Activo destino
fromMedium: string; // Medio origen
toMedium: string; // Medio destino
fromAmount: string; // Monto origen
toAmount: string; // Monto destino
depositInformation: DepositInformation; // Información de depósito
at: number; // Timestamp de creación
graphId: string; // ID del grafo de instrucciones
status: string; // Estado (pending, in_progress, completed, failed)
webhookUrl?: string; // URL de webhook para eventos de la orden
failureReason?: string; // Razón del fallo (cuando status es 'failed')
failureDetails?: string; // Información detallada del fallo
createdAt: string; // Fecha de creación
updatedAt: string; // Fecha de actualización
}
Tipo ExecutionResult
interface ExecutionResult {
nodeId: string; // ID del nodo ejecutado
result: {
status: string; // Estado de la ejecución
args?: unknown[]; // Argumentos adicionales
description?: string; // Descripción del resultado
how?: ExecutionHow; // Instrucciones para completar este paso
};
}
Ejemplo Completo de Flujo PSE
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 realizarSwapPSE() {
const userSession = await bloque.connect('user-alias');
// 1. Obtener lista de bancos
const { banks } = await userSession.swap.pse.banks();
console.log('Bancos disponibles:', banks);
// 2. Buscar tasas disponibles
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 hay tasas disponibles');
return;
}
// 3. Crear orden de swap con PSE y auto-ejecutar
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, // Banco seleccionado por el usuario
userType: 0, // Persona natural
customerEmail: 'user@example.com',
userLegalIdType: 'CC',
userLegalId: '123456789',
customerData: {
fullName: 'User Name',
phoneNumber: '3018362958'
},
},
});
console.log('Orden creada:', result.order.id);
console.log('Estado:', result.order.status);
console.log('Graph ID:', result.order.graphId);
// 4. Redirigir al usuario a PSE si hay URL de redirección
if (result.execution?.result.how?.url) {
console.log('Redirigiendo a PSE...');
window.location.href = result.execution.result.how.url;
}
}
realizarSwapPSE();
Crear Orden de Swap con Bancolombia
El SDK permite crear órdenes de swap usando Kusama como medio de pago origen y Bancolombia como destino. El método bancolombia.create combina la creación de la orden y opcionalmente auto-ejecuta el primer nodo de instrucciones para iniciar el flujo de swap.
Uso Básico
// 1. Buscar tasas disponibles
const rates = await userSession.swap.findRates({
fromAsset: 'COPM/2',
toAsset: 'COP/2',
fromMediums: ['kusama'],
toMediums: ['bancolombia'],
amountSrc: '1000000', // 10,000.00 COP (2 decimales)
});
// 2. Crear orden de swap con Bancolombia
const result = await userSession.swap.bancolombia.create({
rateSig: rates.rates[0].sig,
amountSrc: '1000000',
depositInformation: {
bankAccountType: 'savings',
bankAccountNumber: '5740088718',
bankAccountHolderName: 'david barinas',
bankAccountHolderIdentificationType: 'CC',
bankAccountHolderIdentificationValue: '123456789'
},
args: {
accountUrn: 'did:bloque:card:1231231'
}
});
// 3. Verificar estado de la orden
console.log('Orden creada:', result.order.id);
console.log('Estado:', result.order.status);
Parámetros CreateBancolombiaOrderParams
interface BancolombiaDepositInformation {
bankAccountType: 'savings' | 'checking'; // Tipo de cuenta
bankAccountNumber: string; // Número de cuenta
bankAccountHolderName: string; // Nombre del titular
bankAccountHolderIdentificationType: 'CC' | 'CE' | 'NIT' | 'PP'; // Tipo de ID
bankAccountHolderIdentificationValue: string; // Número de identificación
}
Tipo KusamaAccountArgs
interface KusamaAccountArgs {
accountUrn: string; // URN de cuenta Kusama para fondos origen
}
Ejemplo Completo de Flujo Bancolombia
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 realizarSwapBancolombia() {
const userSession = await bloque.connect('user-alias');
// 1. Buscar tasas disponibles de Kusama a 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 hay tasas disponibles');
return;
}
// 2. Crear orden de swap con detalles bancarios
const result = await userSession.swap.bancolombia.create({
rateSig: rates.rates[0].sig,
amountSrc: '1000000',
type: 'src', // Especificar monto exacto en KSM a pagar
depositInformation: {
bankAccountType: 'savings',
bankAccountNumber: '5740088718',
bankAccountHolderName: 'david barinas',
bankAccountHolderIdentificationType: 'CC',
bankAccountHolderIdentificationValue: '123456789'
},
args: {
accountUrn: 'did:bloque:card:1231231' // Cuenta Kusama origen
}
});
console.log('Orden creada:', result.order.id);
console.log('Estado:', result.order.status);
console.log('Monto Origen:', result.order.fromAmount);
console.log('Monto Destino:', result.order.toAmount);
console.log('Graph ID:', result.order.graphId);
// 3. Monitorear ejecución de la orden (si aplica)
if (result.execution) {
console.log('Ejecución iniciada:', result.execution.nodeId);
}
}
realizarSwapBancolombia();
Tipos de Orden
-
src (por defecto): El usuario especifica el monto exacto a pagar. El monto destino se calcula según el rate.
- Ejemplo: "Quiero pagar exactamente 10,000 COP, dame lo que corresponda en DUSD"
-
dst: El usuario especifica el monto exacto a recibir. El monto origen se calcula según el rate.
- Ejemplo: "Quiero recibir exactamente 5 DUSD, pagaré lo que sea necesario en COP"
-
Guía de Cuentas - Crea y gestiona cuentas para operar swaps
-
Guía de Organizaciones - Administra entidades que pueden realizar swaps
Listado de Órdenes
Obtén todas las órdenes de swap del usuario autenticado:
const orders = await userSession.swap.listOrders();
orders.forEach(order => {
console.log(`${order.id}: ${order.status} — ${order.fromAmount} ${order.fromAsset} → ${order.toAmount} ${order.toAsset}`);
if (order.failureReason) {
console.log(` Error: ${order.failureReason}`);
}
});
ListOrdersParams
Estados y Transiciones
Estados de Orden
stateDiagram-v2
[*] --> pending
pending --> in_progress
pending --> cancelled
pending --> expired
in_progress --> completed
in_progress --> failed
completed --> [*]
failed --> [*]
cancelled --> [*]
expired --> [*]
Estados de Ejecución de Nodos
stateDiagram-v2
[*] --> pending
pending --> running
pending --> timeout
running --> completed
running --> failed
running --> timeout
completed --> [*]
failed --> [*]
timeout --> [*]
Tipo ExecutionHow
Cuando una orden de swap se pausa para acción del usuario, execution.result.how describe qué hacer:
type ExecutionHow = ExecutionHowRedirect | ExecutionHowBrebDeposit;
interface ExecutionHowRedirect {
type: string; // ej. "REDIRECT"
url: string; // URL de checkout PSE
}
interface ExecutionHowBrebDeposit {
type: 'BREB_DEPOSIT';
medium: 'breb';
keyType: string;
keyValue: string;
amount: string;
currency: 'COP';
reference: string;
depositAccountUrn: string;
expectedAmount?: string;
receivedAmount?: string;
remainingAmount?: string;
depositStatus?: 'awaiting' | 'partial';
}
Depósito BRE-B On-Ramp (COP → Kusama)
Deposita COP mediante una llave BRE-B de un solo uso; el escrow paga DUSD en Kusama.
const rates = await userSession.swap.findRates({
fromAsset: 'COP/2',
toAsset: 'DUSD/6',
fromMediums: ['breb'],
toMediums: ['kusama'],
amountSrc: '20000000',
});
const result = await userSession.swap.breb.createDeposit({
rateSig: rates.rates[0].sig,
amountSrc: '20000000',
depositInformation: { urn: card.urn },
args: {},
});
const how = result.execution?.result.how;
if (how?.type === 'BREB_DEPOSIT') {
console.log('Pagar vía BRE-B:', how.keyType, how.keyValue);
console.log('Requerido:', how.expectedAmount ?? how.amount, 'COP (escalado)');
}
Errores: E_INVALID_DEPOSIT_INFORMATION cuando falta urn.
El grafo se reanuda automáticamente cuando el webhook de entrada se liquida (payment.inbound.settled).
Pagos parciales (subpago)
Si el pagador envía menos que order.fromAmount, el grafo sigue pausado en la misma keyValue y callbackToken. Varias transferencias BRE-B a esa llave se suman. Tras cada pago, how se actualiza:
if (how?.type === 'BREB_DEPOSIT' && how.depositStatus === 'partial') {
console.log(`Recibido ${how.receivedAmount}, faltan ${how.remainingAmount}`);
console.log('Complementar en la misma llave:', how.keyValue);
}
Seguimiento: el servidor acumula received_total_scaled en el estado de la instrucción. remainingAmount siempre se deriva (expectedAmount − receivedAmount).
Polling: suscríbete a webhooks swap.order.* o consulta user.swap.listOrders({ graphId: result.order.graphId }). La respuesta inicial de createDeposit no se actualiza cuando llega un pago parcial posterior.
Sobrepago
Si el pagador envía más de lo requerido, el swap entrega el order.toAmount DUSD cotizado. El exceso en COP se marca para reembolso manual; no se convierte en DUSD extra.
Pago RTP (Kusama → Banco US)
Retira DUSD en Kusama a una cuenta bancaria US vía RTP.
const result = await userSession.swap.rtp.create({
rateSig: rates.rates[0].sig,
amountSrc: '100000000',
depositInformation: {
owner: 'Jane Doe',
accountNumber: '1234567890',
routingNumber: '063108680',
accountType: 'checking',
},
});
Errores: E_INVALID_DEPOSIT_INFORMATION cuando faltan campos bancarios requeridos.
On-Ramp Banco US Externo (ACH → Kusama)
Extrae USD de una cuenta bancaria US vinculada vía ACH; los fondos se teleportan a Kusama como DUSD.
const result = await userSession.swap.externalUsBank.create({
rateSig: rates.rates[0].sig,
amountSrc: '10000',
depositInformation: { ledgerAccountId: 'ledger-user-001' },
args: { sourceAccountUrn: 'did:bloque:account:external-us-bank:abc123' },
});
Próximos Pasos