Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ordinariusprof committed Mar 26, 2024
1 parent 0011779 commit 1a350e4
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 26 deletions.
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ OKCOIN_API_PASSPHRASE=
OKCOIN_WITHDRAWAL_WALLET=
OKCOIN_DEPOSIT_WALLET=

# coinbase
COINBASE_API_KEY=
COINBASE_API_SECRET=
# for coinbase, tag is not used for withdrawal, provide the trusted withdrawal address here
COINBASE_WITHDRAWAL_WALLET=
COINBASE_DEPOSIT_WALLET=

# Notifications
SLACK_WEB_HOOK=
Expand Down
40 changes: 17 additions & 23 deletions api/coinbase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ const axios = require('axios');
const crypto = require('crypto');

class CoinbaseAPI {
constructor(apiKey, apiSecret, apiPassphrase) {
constructor(apiKey, apiSecret) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.apiPassphrase = apiPassphrase;
this.baseURL = 'https://api.coinbase.com';
}

Expand Down Expand Up @@ -42,48 +41,43 @@ class CoinbaseAPI {
}

async signMessage(method, endpoint, body) {
const cb_access_timestamp = Date.now() / 1000; // in ms

// create the prehash string by concatenating required parts
const message = `${cb_access_timestamp}${method}${endpoint}${JSON.stringify(body)}`;

// decode the base64 secret
const key = Buffer.from(this.apiSecret, 'base64');

// create a sha256 hmac with the secret
const hmac = crypto.createHmac('sha256', key);

// sign the required message with the hmac and base64 encode the result
const cb_access_sign = hmac.update(message).digest('base64');

const timestamp = Math.floor(Date.now() / 1000); // Unix time in seconds
const message = `${timestamp}${method}${endpoint}${JSON.stringify(body)}`;
const signature = crypto.createHmac('sha256', this.apiSecret).update(message).digest('hex');
return {
'CB-ACCESS-KEY': this.apiKey,
'CB-ACCESS-SIGN': cb_access_sign,
'CB-ACCESS-TIMESTAMP': cb_access_timestamp,
'CB-ACCESS-PASSPHRASE': this.apiPassphrase,
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'Content-Type': 'application/json',
};
}

async getSystemStatus() {
return this.getPublicEndpoint('/system/status');
throw new Error('Not implemented');
}

async getAccountId() {
const accounts = await this.getAuthEndpoint('/v2/accounts/BTC');
return accounts.data.data.id;
}

async getAccountBalance() {
return this.getAuthEndpoint('/accounts');
const accountId = await this.getAccountId();
return this.getAuthEndpoint(`/v2/accounts/${accountId}`);
}

async withdrawFunds(amount, currency, address) {
const accountId = await this.getAccountId();
const body = {
amount,
currency,
crypto_address: address,
};
return this.postAuthEndpoint('/withdrawals/crypto', body);
return this.postAuthEndpoint(`/v2/accounts/${accountId}/transactions`, body);
}

async getServerTime() {
return this.getPublicEndpoint('/time');
return this.getPublicEndpoint('/v2/time');
}
}

Expand Down
29 changes: 29 additions & 0 deletions conf/satminer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ console.log('MIN_DEPOSIT_AMOUNT', MIN_DEPOSIT_AMOUNT);
let EXCHANGE_DATA = {
ACTIVE_EXCHANGE: process.env.ACTIVE_EXCHANGE,
};
let EXCHANGE_DEPOSIT_WALLET;
if (process.env.ACTIVE_EXCHANGE === 'kraken') {
const { KRAKEN_API_KEY, KRAKEN_API_SECRET } = process.env;
if (!KRAKEN_API_KEY || !KRAKEN_API_SECRET) {
Expand All @@ -51,6 +52,7 @@ if (process.env.ACTIVE_EXCHANGE === 'kraken') {
KRAKEN_WITHDRAW_CURRENCY,
KRAKEN_DEPOSIT_WALLET,
};
EXCHANGE_DEPOSIT_WALLET = KRAKEN_DEPOSIT_WALLET;
} else if (process.env.ACTIVE_EXCHANGE === 'okcoin') {
const { OKCOIN_API_KEY, OKCOIN_API_SECRET, OKCOIN_API_PASSPHRASE } = process.env;
if (!OKCOIN_API_KEY || !OKCOIN_API_SECRET || !OKCOIN_API_PASSPHRASE) {
Expand All @@ -75,6 +77,32 @@ if (process.env.ACTIVE_EXCHANGE === 'kraken') {
OKCOIN_WITHDRAW_CURRENCY,
OKCOIN_DEPOSIT_WALLET,
};
EXCHANGE_DEPOSIT_WALLET = OKCOIN_DEPOSIT_WALLET;
} else if (process.env.ACTIVE_EXCHANGE === 'coinbase') {
const { COINBASE_API_KEY, COINBASE_API_SECRET } = process.env;
if (!COINBASE_API_KEY || !COINBASE_API_SECRET) {
throw Error('Missing COINBASE_API_KEY or COINBASE_API_SECRET environment variable');
}
const { COINBASE_WITHDRAWAL_WALLET } = process.env;
if (!COINBASE_WITHDRAWAL_WALLET) {
throw Error('Missing COINBASE_WITHDRAWAL_WALLET environment variable');
}
console.log('COINBASE_WITHDRAWAL_WALLET', COINBASE_WITHDRAWAL_WALLET);
const COINBASE_WITHDRAW_CURRENCY = 'BTC';
const { COINBASE_DEPOSIT_WALLET } = process.env;
if (!COINBASE_DEPOSIT_WALLET) {
throw new Error('Missing COINBASE_DEPOSIT_WALLET environment variable');
}
EXCHANGE_DATA = {
...EXCHANGE_DATA,
COINBASE_API_KEY,
COINBASE_API_SECRET,
COINBASE_WITHDRAWAL_WALLET,
COINBASE_WITHDRAW_CURRENCY,
COINBASE_DEPOSIT_WALLET,
};
EXCHANGE_DEPOSIT_WALLET = COINBASE_DEPOSIT_WALLET;

} else {
throw new Error('Unknown exchange');
}
Expand Down Expand Up @@ -170,6 +198,7 @@ console.log('CUSTOM_SPECIAL_SAT_WALLETS', CUSTOM_SPECIAL_SAT_WALLETS);
module.exports = {
ORDINALSBOT_API_KEY,
EXCHANGE_DATA,
EXCHANGE_DEPOSIT_WALLET,
MAX_WITHDRAWAL_AMOUNT,
MIN_WITHDRAWAL_AMOUNT,
MIN_DEPOSIT_AMOUNT,
Expand Down
22 changes: 19 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ const { Satscanner, Satextractor, Mempool } = require('ordinalsbot');
const Satminer = require('./model/satminer');
const KrakenAPI = require('./api/kraken');
const OkcoinAPI = require('./api/okcoin');
const CoinbaseAPI = require('./api/coinbase');
const MempoolApi = require('./api/mempool');
const KrakenTumbler = require('./model/exchanges/kraken');
const OkcoinTumbler = require('./model/exchanges/okcoin');
const CoinbaseTumbler = require('./model/exchanges/coinbase');
const Wallet = require('./model/wallet');
const { loadBitcoinWallet } = require('./utils/funcs');
const {
EXCHANGE_DATA,
EXCHANGE_DEPOSIT_WALLET,
MAX_WITHDRAWAL_AMOUNT,
MIN_WITHDRAWAL_AMOUNT,
MIN_DEPOSIT_AMOUNT,
Expand Down Expand Up @@ -58,13 +61,16 @@ const {
KRAKEN_API_SECRET,
KRAKEN_WITHDRAWAL_WALLET,
KRAKEN_WITHDRAW_CURRENCY,
KRAKEN_DEPOSIT_WALLET,
EXCHANGE_DEPOSIT_WALLET,
OKCOIN_API_KEY,
OKCOIN_API_SECRET,
OKCOIN_API_PASSPHRASE,
OKCOIN_WITHDRAWAL_WALLET,
OKCOIN_WITHDRAW_CURRENCY,
OKCOIN_DEPOSIT_WALLET,
COINBASE_API_KEY,
COINBASE_API_SECRET,
COINBASE_WITHDRAWAL_WALLET,
COINBASE_WITHDRAW_CURRENCY,
} = EXCHANGE_DATA;

const sweepConfirmationTargetBlocks = 1;
Expand All @@ -74,7 +80,7 @@ const satminer = new Satminer(
satextractor,
TUMBLER_ADDRESS,
INVENTORY_WALLET,
ACTIVE_EXCHANGE === 'kraken' ? KRAKEN_DEPOSIT_WALLET : OKCOIN_DEPOSIT_WALLET,
EXCHANGE_DEPOSIT_WALLET,
sweepConfirmationTargetBlocks,
MIN_DEPOSIT_AMOUNT,
slackWebHook,
Expand Down Expand Up @@ -104,6 +110,16 @@ switch (ACTIVE_EXCHANGE) {
OKCOIN_WITHDRAW_CURRENCY,
);
break;
case 'coinbase':
const coinbaseAPI = new CoinbaseAPI(COINBASE_API_KEY, COINBASE_API_SECRET);
exchangeTumbler = new CoinbaseTumbler(
coinbaseAPI,
MIN_WITHDRAWAL_AMOUNT,
MAX_WITHDRAWAL_AMOUNT,
COINBASE_WITHDRAWAL_WALLET,
COINBASE_WITHDRAW_CURRENCY,
);
break;
default:
throw new Error(`Unknown exchange ${ACTIVE_EXCHANGE}`);
}
Expand Down
68 changes: 68 additions & 0 deletions model/exchanges/coinbase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const CoinbaseAPI = require('../../api/coinbase');

/**
* Rotates funds from a Coinbase account
*/
class CoinbaseTumbler {
/**
* @param {CoinbaseAPI} coinbaseClient
* @param {number} minWithdrawalAmount
* @param {number} maxWithdrawalAmount
* @param {string} withdrawWallet
* @param {string} withdrawCurrency
*/
constructor(
coinbaseClient,
minWithdrawalAmount,
maxWithdrawalAmount,
withdrawWallet,
withdrawCurrency,
) {
this.coinbaseClient = coinbaseClient;
this.minWithdrawalAmount = minWithdrawalAmount;
this.maxWithdrawalAmount = maxWithdrawalAmount;
this.withdrawWallet = withdrawWallet;
this.withdrawCurrency = withdrawCurrency;
}

withdrawAvailableFunds = async () => {
const btcBalance = balance.data.find((b) => b.ccy === 'BTC').availBal;
if (btcBalance < this.minWithdrawalAmount) {
console.log(`insufficient funds to withdraw, account balance ${btcBalance}`);
return false;
}

let withdrawalAmount = Number(btcBalance).toFixed(8);;
if (btcBalance > this.maxWithdrawalAmount) {
withdrawalAmount = this.maxWithdrawalAmount;
}

const withdrawalFees = await this.coinbaseClient.getWithdrawalFee(this.withdrawCurrency);
let btcFee = withdrawalFees.data.find((f) => f.chain === 'BTC-Bitcoin').maxFee;
btcFee = parseFloat(btcFee);
withdrawalAmount -= btcFee;
withdrawalAmount = Number(withdrawalAmount).toFixed(8);

console.log(
`withdrawing ${withdrawalAmount} ${this.withdrawCurrency} to wallet ${this.withdrawWallet}`,
);

const res = await this.coinbaseClient.withdrawFunds(
this.withdrawCurrency,
this.withdrawWallet,
withdrawalAmount,
btcFee,
);
console.log('response from coinbase', res);

if (res.msg) {
console.error('error calling coinbase api', res.msg);
return false;
}

console.log('successful withdrawal from coinbase');
return true;
};
}

module.exports = CoinbaseTumbler;

0 comments on commit 1a350e4

Please sign in to comment.