Transfer

The transfer method creates and optionally broadcasts MNEE token transfers. It handles all the complexity of creating valid MNEE transactions, including UTXO selection, fee calculation, and cosigner authorization.

Usage

Basic Transfer

const recipients = [{ address: 'recipient-address', amount: 2.55 }];
const wif = 'sender-wif-key';

const response = await mnee.transfer(recipients, wif);
console.log('Ticket ID:', response.ticketId);

Multiple Recipients

const recipients = [
  { address: 'recipient-1-address', amount: 2.55 },
  { address: 'recipient-2-address', amount: 5 },
  { address: 'recipient-3-address', amount: 0.75 },
];
const wif = 'sender-wif-key';

const response = await mnee.transfer(recipients, wif);
console.log('Ticket ID:', response.ticketId);

// Check status of the transfer
const status = await mnee.getTxStatus(response.ticketId);
console.log('Status:', status);

Create Without Broadcasting

const recipients = [{ address: 'recipient-address', amount: 10 }];

// Set broadcast to false to create but not submit
const response = await mnee.transfer(recipients, wif, { broadcast: false });
console.log('Raw transaction:', response.rawtx);
// Ticket ID will not be available when broadcast is false

Transfer with Webhook Callback

const recipients = [{ address: 'recipient-address', amount: 10 }];

// Provide webhook URL for async status updates
const response = await mnee.transfer(recipients, wif, {
  broadcast: true,
  callbackUrl: 'https://your-api.com/webhook/mnee'
});

console.log('Ticket ID:', response.ticketId);
// Your webhook will receive status updates as the transaction progresses

Parameters

  • request: Array of SendMNEE objects, each containing:

    • address: Recipient Bitcoin address

    • amount: Amount to send in MNEE (not atomic units)

  • wif: Wallet Import Format private key of the sender

  • transferOptions (optional): Object containing:

    • broadcast: Whether to broadcast the transaction (default: true)

    • callbackUrl: Webhook URL for status updates (only when broadcast is true)

Response

Returns a TransferResponse object:

{
  ticketId?: string;  // Ticket ID for tracking (only if broadcast is true)
  rawtx?: string;     // The raw transaction hex (only if broadcast is false)
}

Common Use Cases

Simple Payment

async function payInvoice(recipientAddress, amountMNEE, senderWif) {
  try {
    const response = await mnee.transfer([{ address: recipientAddress, amount: amountMNEE }], senderWif);
    console.log(`Payment sent! Ticket: ${response.ticketId}`);

    // Get transaction ID from status
    const status = await mnee.getTxStatus(response.ticketId);
    return status.tx_id;
  } catch (error) {
    console.error('Payment failed:', error.message);
    throw error;
  }
}

Batch Payments

async function distributePayments(payments, senderWif) {
  // payments is array of {address, amount}
  try {
    const response = await mnee.transfer(payments, senderWif);
    console.log(`Distributed to ${payments.length} recipients`);
    console.log(`Ticket ID: ${response.ticketId}`);

    // Log each payment
    payments.forEach((p) => {
      console.log(`  - ${p.address}: ${p.amount} MNEE`);
    });

    return response.ticketId;
  } catch (error) {
    console.error('Distribution failed:', error.message);
    throw error;
  }
}

Two-Step Transfer with Validation

async function secureTransfer(recipients, wif) {
  // Step 1: Create transaction without broadcasting
  const txResponse = await mnee.transfer(recipients, wif, { broadcast: false });

  // Step 2: Validate the transaction
  const isValid = await mnee.validateMneeTx(txResponse.rawtx, recipients);
  if (!isValid) {
    throw new Error('Transaction validation failed');
  }

  // Step 3: Parse to review
  const parsed = await mnee.parseTxFromRawTx(txResponse.rawtx);
  console.log('Transaction details:', parsed);

  // Step 4: Broadcast if everything looks good
  const submitResponse = await mnee.submitRawTx(txResponse.rawtx);
  return submitResponse.ticketId;
}

Transfer with Balance Check

async function safeTransfer(recipients, wif, senderAddress) {
  // Calculate total needed
  const totalNeeded = recipients.reduce((sum, r) => sum + r.amount, 0);

  // Check balance
  const balance = await mnee.balance(senderAddress);
  if (balance.decimalAmount < totalNeeded) {
    throw new Error(`Insufficient balance. Have ${balance.decimalAmount}, need ${totalNeeded} MNEE`);
  }

  // Proceed with transfer
  const response = await mnee.transfer(recipients, wif);
  console.log(`Transfer complete: ${response.ticketId}`);

  // Get transaction ID
  const status = await mnee.getTxStatus(response.ticketId);
  return status.tx_id;
}

Micro-Payment Channel

async function sendMicroPayment(address, amount, wif) {
  const MIN_AMOUNT = 0.001; // 0.001 MNEE minimum

  if (amount < MIN_AMOUNT) {
    throw new Error(`Amount too small. Minimum is ${MIN_AMOUNT} MNEE`);
  }

  const response = await mnee.transfer([{ address, amount }], wif);

  // Get transaction ID from status
  const status = await mnee.getTxStatus(response.ticketId);

  return {
    txid: status.tx_id,
    ticketId: response.ticketId,
    amount: amount,
    timestamp: new Date().toISOString(),
  };
}

Error Handling

The transfer method can throw several specific errors:

try {
  const response = await mnee.transfer(recipients, wif);
} catch (error) {
  switch (true) {
    case error.message.includes('Config not fetched'):
      console.error('Failed to fetch cosigner configuration');
      break;
    case error.message.includes('Invalid transfer options'):
      console.error('Invalid recipients or amounts');
      break;
    case error.message.includes('Private key not found'):
      console.error('Invalid WIF private key');
      break;
    case error.message.includes('Invalid amount'):
      console.error('Amount must be greater than 0');
      break;
    case error.message.includes('Insufficient MNEE balance'):
      console.error('Not enough MNEE tokens');
      break;
    case error.message.includes('Failed to broadcast transaction'):
      console.error('Cosigner rejected the transaction');
      break;
    case error.message.includes('Invalid API key'):
      console.error('API key authentication failed (401/403)');
      break;
    case error.message.includes('HTTP error! status:'):
      console.error('API request failed:', error.message);
      break;
    default:
      console.error('Transfer failed:', error.message);
  }
}

Important Notes

  • Amounts are specified in MNEE, not atomic units (1 MNEE = 100,000 atomic units)

  • The method automatically:

    • Selects appropriate UTXOs

    • Calculates fees based on transaction size

    • Adds change output if needed

    • Obtains cosigner authorization

  • Minimum transfer amount is determined by dust limit (check via config())

  • All recipients must have valid Bitcoin addresses

  • The sender must have sufficient balance to cover amounts + fees

  • When broadcast is true, the transaction is processed asynchronously and you receive a ticketId to track status

See Also

Last updated