Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ready: monitor the funding account of vortex #277

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@rainbow-me/rainbowkit": "^2.1.7",
"@sentry/react": "^8.36.0",
"@sentry/vite-plugin": "^2.22.6",
"@slack/web-api": "^7.7.0",
"@talismn/connect-components": "^1.1.8",
"@talismn/connect-wallets": "^1.2.5",
"@tanstack/react-query": "^5.45.1",
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 this account ${moonbeamExecutorAccount.address}.`,
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
});
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 @@ -14,7 +14,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
30 changes: 30 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 @@ -108,14 +126,26 @@ 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(
remainingMaxSubsidiesAvailable,
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
).toString()} ${token} please charge this account ${fundingAccountKeypair.address}.`,
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
});
}
}),
);

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 };
}
slackNotifier.sendMessage({
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
text: `Current balance of funding account is ${divideByPowerOfTen(
nativeBalance,
).toString()} PEN please charge this account ${fundingAccountKeypair.address}.`,
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
});
return { status: false, public: fundingAccountKeypair.address };
};
27 changes: 27 additions & 0 deletions signer-service/src/api/services/slack.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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');
}
}

async sendMessage(message) {
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}`);
}
}
}

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 @@ -7,6 +7,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 this account ${FUNDING_PUBLIC_KEY}.`,
Sharqiewicz marked this conversation as resolved.
Show resolved Hide resolved
});
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
99 changes: 97 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4480,6 +4480,42 @@ __metadata:
languageName: node
linkType: hard

"@slack/logger@npm:^4.0.0":
version: 4.0.0
resolution: "@slack/logger@npm:4.0.0"
dependencies:
"@types/node": "npm:>=18.0.0"
checksum: 10/dc79e9d2032c4bf9ce01d96cc72882f003dd376d036f172d4169662cfc2c9b384a80d5546b06021578dd473e7059f064303f0ba851eeb153387f2081a1e3062e
languageName: node
linkType: hard

"@slack/types@npm:^2.9.0":
version: 2.14.0
resolution: "@slack/types@npm:2.14.0"
checksum: 10/fa24a113b88e087f899078504c2ba50ab9795f7c2dd1a2d95b28217a3af20e554494f9cc3b8c8ce173120990d98e19400c95369f9067cecfcc46c08b59d2a46f
languageName: node
linkType: hard

"@slack/web-api@npm:^7.7.0":
version: 7.7.0
resolution: "@slack/web-api@npm:7.7.0"
dependencies:
"@slack/logger": "npm:^4.0.0"
"@slack/types": "npm:^2.9.0"
"@types/node": "npm:>=18.0.0"
"@types/retry": "npm:0.12.0"
axios: "npm:^1.7.4"
eventemitter3: "npm:^5.0.1"
form-data: "npm:^4.0.0"
is-electron: "npm:2.2.2"
is-stream: "npm:^2"
p-queue: "npm:^6"
p-retry: "npm:^4"
retry: "npm:^0.13.1"
checksum: 10/465031984036532eb7291df9b686ea143795ecfc3b235e2b759ea8790965abbee9da349c6184a49ec0425d76251d9e35237ee0b1c8cf20dcac1df82db8f95b93
languageName: node
linkType: hard

"@socket.io/component-emitter@npm:~3.1.0":
version: 3.1.2
resolution: "@socket.io/component-emitter@npm:3.1.2"
Expand Down Expand Up @@ -5038,6 +5074,15 @@ __metadata:
languageName: node
linkType: hard

"@types/node@npm:>=18.0.0":
version: 22.9.0
resolution: "@types/node@npm:22.9.0"
dependencies:
undici-types: "npm:~6.19.8"
checksum: 10/a7df3426891868b0f5fb03e46aeddd8446178233521c624a44531c92a040cf08a82d8235f7e1e02af731fd16984665d4d71f3418caf9c2788313b10f040d615d
languageName: node
linkType: hard

"@types/parse-json@npm:^4.0.0":
version: 4.0.0
resolution: "@types/parse-json@npm:4.0.0"
Expand Down Expand Up @@ -5092,6 +5137,13 @@ __metadata:
languageName: node
linkType: hard

"@types/retry@npm:0.12.0":
version: 0.12.0
resolution: "@types/retry@npm:0.12.0"
checksum: 10/bbd0b88f4b3eba7b7acfc55ed09c65ef6f2e1bcb4ec9b4dca82c66566934351534317d294a770a7cc6c0468d5573c5350abab6e37c65f8ef254443e1b028e44d
languageName: node
linkType: hard

"@types/scheduler@npm:*":
version: 0.16.3
resolution: "@types/scheduler@npm:0.16.3"
Expand Down Expand Up @@ -6491,6 +6543,17 @@ __metadata:
languageName: node
linkType: hard

"axios@npm:^1.7.4":
version: 1.7.7
resolution: "axios@npm:1.7.7"
dependencies:
follow-redirects: "npm:^1.15.6"
form-data: "npm:^4.0.0"
proxy-from-env: "npm:^1.1.0"
checksum: 10/7f875ea13b9298cd7b40fd09985209f7a38d38321f1118c701520939de2f113c4ba137832fe8e3f811f99a38e12c8225481011023209a77b0c0641270e20cde1
languageName: node
linkType: hard

"babel-plugin-polyfill-corejs2@npm:^0.3.3":
version: 0.3.3
resolution: "babel-plugin-polyfill-corejs2@npm:0.3.3"
Expand Down Expand Up @@ -10638,6 +10701,13 @@ __metadata:
languageName: node
linkType: hard

"is-electron@npm:2.2.2":
version: 2.2.2
resolution: "is-electron@npm:2.2.2"
checksum: 10/de5aa8bd8d72c96675b8d0f93fab4cc21f62be5440f65bc05c61338ca27bd851a64200f31f1bf9facbaa01b3dbfed7997b2186741d84b93b63e0aff1db6a9494
languageName: node
linkType: hard

"is-extglob@npm:^2.1.1":
version: 2.1.1
resolution: "is-extglob@npm:2.1.1"
Expand Down Expand Up @@ -10814,7 +10884,7 @@ __metadata:
languageName: node
linkType: hard

"is-stream@npm:^2.0.0":
"is-stream@npm:^2, is-stream@npm:^2.0.0":
version: 2.0.1
resolution: "is-stream@npm:2.0.1"
checksum: 10/b8e05ccdf96ac330ea83c12450304d4a591f9958c11fd17bed240af8d5ffe08aedafa4c0f4cfccd4d28dc9d4d129daca1023633d5c11601a6cbc77521f6fae66
Expand Down Expand Up @@ -12777,7 +12847,7 @@ __metadata:
languageName: node
linkType: hard

"p-queue@npm:^6.6.2":
"p-queue@npm:^6, p-queue@npm:^6.6.2":
version: 6.6.2
resolution: "p-queue@npm:6.6.2"
dependencies:
Expand All @@ -12787,6 +12857,16 @@ __metadata:
languageName: node
linkType: hard

"p-retry@npm:^4":
version: 4.6.2
resolution: "p-retry@npm:4.6.2"
dependencies:
"@types/retry": "npm:0.12.0"
retry: "npm:^0.13.1"
checksum: 10/45c270bfddaffb4a895cea16cb760dcc72bdecb6cb45fef1971fa6ea2e91ddeafddefe01e444ac73e33b1b3d5d29fb0dd18a7effb294262437221ddc03ce0f2e
languageName: node
linkType: hard

"p-timeout@npm:^3.2.0":
version: 3.2.0
resolution: "p-timeout@npm:3.2.0"
Expand Down Expand Up @@ -14433,6 +14513,13 @@ __metadata:
languageName: node
linkType: hard

"retry@npm:^0.13.1":
version: 0.13.1
resolution: "retry@npm:0.13.1"
checksum: 10/6125ec2e06d6e47e9201539c887defba4e47f63471db304c59e4b82fc63c8e89ca06a77e9d34939a9a42a76f00774b2f46c0d4a4cbb3e287268bd018ed69426d
languageName: node
linkType: hard

"reusify@npm:^1.0.4":
version: 1.0.4
resolution: "reusify@npm:1.0.4"
Expand Down Expand Up @@ -16063,6 +16150,13 @@ __metadata:
languageName: node
linkType: hard

"undici-types@npm:~6.19.8":
version: 6.19.8
resolution: "undici-types@npm:6.19.8"
checksum: 10/cf0b48ed4fc99baf56584afa91aaffa5010c268b8842f62e02f752df209e3dea138b372a60a963b3b2576ed932f32329ce7ddb9cb5f27a6c83040d8cd74b7a70
languageName: node
linkType: hard

"unenv@npm:^1.9.0":
version: 1.9.0
resolution: "unenv@npm:1.9.0"
Expand Down Expand Up @@ -16741,6 +16835,7 @@ __metadata:
"@rainbow-me/rainbowkit": "npm:^2.1.7"
"@sentry/react": "npm:^8.36.0"
"@sentry/vite-plugin": "npm:^2.22.6"
"@slack/web-api": "npm:^7.7.0"
"@talismn/connect-components": "npm:^1.1.8"
"@talismn/connect-wallets": "npm:^1.2.5"
"@tanstack/react-query": "npm:^5.45.1"
Expand Down
Loading