Skip to content

Commit

Permalink
Merge pull request #277 from pendulum-chain/216-monitor-the-funding-a…
Browse files Browse the repository at this point in the history
…ccount-of-vortex

Ready: monitor the funding account of vortex
  • Loading branch information
Sharqiewicz authored Jan 3, 2025
2 parents f698b86 + e39acb9 commit a88123d
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 12 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ docs/
.lighthouseci/
**/coins/*
*.yml
*.sol

package-lock.json
package.json
Expand Down
2 changes: 1 addition & 1 deletion _redirects
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/api/production/* https://prototype-signer-service-polygon.pendulumchain.tech/:splat 200
/api/staging/* https://prototype-signer-service-polygon-staging.pendulumchain.tech/:splat 200
/* /index.html 200
/* /index.html 200
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"@reown/appkit-adapter-wagmi": "^1.3.1",
"@sentry/react": "^8.36.0",
"@sentry/vite-plugin": "^2.22.6",
"@slack/web-api": "^7.7.0",
"@talismn/connect-components": "^1.1.9",
"@talismn/connect-wallets": "^1.2.5",
"@tanstack/react-query": "^5.61.0",
"@walletconnect/modal": "^2.6.2",
"@walletconnect/universal-provider": "^2.12.2",
Expand Down
1 change: 1 addition & 0 deletions signer-service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The following environment variables are available to configure the service.
- `FUNDING_SECRET`: Secret key to sign the funding transactions on Stellar.
- `PENDULUM_FUNDING_SEED`: Seed phrase to sign the funding transactions on Pendulum.
- `MOONBEAM_EXECUTOR_PRIVATE_KEY`: Private key to sign the transactions on Moonbeam.
- `SLACK_WEB_HOOK_TOKEN` - Slack web hook token for error reporting.

### Optional

Expand Down
14 changes: 10 additions & 4 deletions signer-service/src/api/controllers/moonbeam.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
MOONBEAM_RECEIVER_CONTRACT_ADDRESS,
MOONBEAM_FUNDING_AMOUNT_UNITS,
} = require('../../constants/constants');
const { SlackNotifier } = require('../services/slack.service');
const splitReceiverABI = require('../../../../mooncontracts/splitReceiverABI.json');

exports.executeXcmController = async (req, res) => {
Expand Down Expand Up @@ -54,6 +55,7 @@ exports.executeXcmController = async (req, res) => {
};

exports.sendStatusWithPk = async () => {
const slackService = new SlackNotifier();
let moonbeamExecutorAccount;

try {
Expand All @@ -66,12 +68,16 @@ exports.sendStatusWithPk = async () => {
const balance = await publicClient.getBalance({ address: moonbeamExecutorAccount.address });

// We are checking if the balance is less than 10 GLMR
const minimum_balance = Big(MOONBEAM_FUNDING_AMOUNT_UNITS).times(Big(10).pow(18));
if (balance < minimum_balance) {
const minimumBalance = Big(MOONBEAM_FUNDING_AMOUNT_UNITS).times(Big(10).pow(18));

if (balance < minimumBalance) {
slackService.sendMessage({
text: `Current balance of funding account is ${balance} GLMR please charge the account ${moonbeamExecutorAccount.address}.`,
});
return { status: false, public: moonbeamExecutorAccount.address };
} else {
return { status: true, public: moonbeamExecutorAccount.address };
}

return { status: true, public: moonbeamExecutorAccount.address };
} catch (error) {
console.error('Error fetching Moonbeam executor balance:', error);
return { status: false, public: moonbeamExecutorAccount?.address };
Expand Down
2 changes: 1 addition & 1 deletion signer-service/src/api/routes/v1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const { sendStatusWithPk: sendStellarStatusWithPk } = require('../../services/st
const { sendStatusWithPk: sendPendulumStatusWithPk } = require('../../services/pendulum.service');
const { sendStatusWithPk: sendMoonbeamStatusWithPk } = require('../../controllers/moonbeam.controller');

async function sendStatusWithPk(req, res, next) {
async function sendStatusWithPk(_, res) {
const stellar = await sendStellarStatusWithPk();
const pendulum = await sendPendulumStatusWithPk();
const moonbeam = await sendMoonbeamStatusWithPk();
Expand Down
32 changes: 32 additions & 0 deletions signer-service/src/api/services/pendulum.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
PENDULUM_EPHEMERAL_STARTING_BALANCE_UNITS,
} = require('../../constants/constants');
const { TOKEN_CONFIG } = require('../../constants/tokenConfig');
const { SlackNotifier } = require('./slack.service');

require('dotenv').config();

Expand All @@ -21,6 +22,14 @@ function multiplyByPowerOfTen(bigDecimal, power) {
return newBigDecimal;
}

function divideByPowerOfTen(bigDecimal, power) {
const newBigDecimal = new Big(bigDecimal);
if (newBigDecimal.c[0] === 0) return newBigDecimal;

newBigDecimal.e -= power;
return newBigDecimal;
}

let api;
let previousSpecVersion;

Expand Down Expand Up @@ -82,7 +91,16 @@ exports.fundEphemeralAccount = async (ephemeralAddress) => {
}
};

const ChainDecimals = 12;

const nativeToDecimal = (value, decimals = ChainDecimals) => {
const divisor = new Big(10).pow(decimals);

return value.div(divisor);
};

exports.sendStatusWithPk = async () => {
const slackNotifier = new SlackNotifier();
const apiData = await createPolkadotApi();
const { fundingAccountKeypair } = getFundingData(apiData.ss58Format, apiData.decimals);
const { data: balance } = await apiData.api.query.system.account(fundingAccountKeypair.address);
Expand All @@ -109,14 +127,28 @@ exports.sendStatusWithPk = async () => {
if (remainingMaxSubsidiesAvailable.lt(SUBSIDY_MINIMUM_RATIO_FUND_UNITS)) {
isTokensSufficient = false;
console.log(`Token ${token} balance is insufficient.`);

slackNotifier.sendMessage({
text: `Current balance of funding account is ${nativeToDecimal(
tokenBalance,
).toString()} ${token} please charge the account ${fundingAccountKeypair.address}.`,
});
}
}),
);

const minimumBalanceFundingAccount = multiplyByPowerOfTen(Big(PENDULUM_FUNDING_AMOUNT_UNITS), apiData.decimals);
const nativeBalance = Big(balance?.free?.toString() ?? '0');

if (nativeBalance.gte(minimumBalanceFundingAccount) && isTokensSufficient) {
return { status: true, public: fundingAccountKeypair.address };
}
if (nativeBalance.lt(minimumBalanceFundingAccount)) {
slackNotifier.sendMessage({
text: `Current balance of funding account is ${nativeToDecimal(
nativeBalance,
).toString()} PEN please charge the account ${fundingAccountKeypair.address}.`,
});
}
return { status: false, public: fundingAccountKeypair.address };
};
56 changes: 56 additions & 0 deletions signer-service/src/api/services/slack.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Store last message timestamps with their message signatures
const messageHistory = new Map();
// 6 hours in milliseconds
const cooldownPeriod = 6 * 60 * 60 * 1000;

class SlackNotifier {
constructor() {
if (process.env.SLACK_WEB_HOOK_TOKEN) {
this.webhookUrl = `https://hooks.slack.com/services/${process.env.SLACK_WEB_HOOK_TOKEN}`;
} else {
throw new Error('SLACK_WEB_HOOK_TOKEN is not defined');
}
}

generateMessageSignature(message) {
// Create a unique signature for the message
return JSON.stringify(message);
}

isMessageAllowed(signature) {
const now = Date.now();
const lastSent = messageHistory.get(signature);

if (!lastSent) return true;

return now - lastSent >= cooldownPeriod;
}

async sendMessage(message) {
const signature = this.generateMessageSignature(message);

if (!this.isMessageAllowed(signature)) {
// Message is still in cooldown period, skip sending
return;
}

const payload = JSON.stringify(message);

const response = await fetch(this.webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: payload,
});

if (!response.ok) {
throw new Error(`Failed to send message. Status: ${response.status}`);
}

// Update the timestamp for this message
messageHistory.set(signature, Date.now());
}
}

exports.SlackNotifier = SlackNotifier;
13 changes: 10 additions & 3 deletions signer-service/src/api/services/stellar.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const {
STELLAR_EPHEMERAL_STARTING_BALANCE_UNITS,
} = require('../../constants/constants');
const { TOKEN_CONFIG, getTokenConfigByAssetCode } = require('../../constants/tokenConfig');
const { SlackNotifier } = require('./slack.service');

// Derive funding pk
const FUNDING_PUBLIC_KEY = Keypair.fromSecret(FUNDING_SECRET).publicKey();
const horizonServer = new Horizon.Server(HORIZON_URL);
Expand Down Expand Up @@ -132,14 +134,19 @@ async function buildPaymentAndMergeTx(
}

async function sendStatusWithPk() {
const slackNotifier = new SlackNotifier();
let stellarBalance = null;

try {
// ensure the funding account exists
const horizonServer = new Horizon.Server(HORIZON_URL);
let account = await horizonServer.loadAccount(FUNDING_PUBLIC_KEY);
let stellarBalance = account.balances.find((balance) => balance.asset_type === 'native');
const account = await horizonServer.loadAccount(FUNDING_PUBLIC_KEY);
stellarBalance = account.balances.find((balance) => balance.asset_type === 'native');

// ensure we have at the very least 10 XLM in the account
if (Number(stellarBalance.balance) < STELLAR_FUNDING_AMOUNT_UNITS) {
slackNotifier.sendMessage({
text: `Current balance of funding account is ${stellarBalance.balance} XLM please charge the account ${FUNDING_PUBLIC_KEY}.`,
});
return { status: false, public: FUNDING_PUBLIC_KEY };
}

Expand Down
1 change: 0 additions & 1 deletion signer-service/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const { Keypair } = require('stellar-sdk');
const { port, env } = require('./config/vars');
const logger = require('./config/logger');
const app = require('./config/express');
Expand Down
Loading

0 comments on commit a88123d

Please sign in to comment.