Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env-sample
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ ORDER_PUBLISHED_EXPIRATION_WINDOW=82800

# Minimum amount for a payment in satoshis
MIN_PAYMENT_AMT=1
# Maximum amount for a payment in satoshis
MAX_PAYMENT_AMT=10000000

# Maximum number of orders that a user can have published (PENDING) at the same time
MAX_PENDING_ORDERS=4
Expand Down
18 changes: 18 additions & 0 deletions bot/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,23 @@ const mustBeGreatherEqThan = async (
}
};

const mustBeLessEqThan = async (
ctx: MainContext,
fieldName: string,
qty: number,
) => {
try {
await ctx.reply(
ctx.i18n.t('must_be_lt_or_eq', {
fieldName,
qty,
}),
);
} catch (error) {
logger.error(error);
}
};

const bannedUserErrorMessage = async (ctx: MainContext, user: UserDocument) => {
try {
await ctx.telegram.sendMessage(
Expand Down Expand Up @@ -2149,6 +2166,7 @@ export {
termsMessage,
privacyMessage,
mustBeGreatherEqThan,
mustBeLessEqThan,
bannedUserErrorMessage,
fiatSentMessages,
orderOnfiatSentStatusMessages,
Expand Down
18 changes: 18 additions & 0 deletions bot/modules/orders/scenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,24 @@ const createOrderHandlers = {
await ctx.wizard.state.updateUI();
return;
}
const minPaymentAmt = Number(process.env.MIN_PAYMENT_AMT) || 0;
const maxPaymentAmt = Number(process.env.MAX_PAYMENT_AMT) || 0;
if (input !== 0 && minPaymentAmt > 0 && input < minPaymentAmt) {
ctx.wizard.state.error = ctx.i18n.t('must_be_gt_or_eq', {
fieldName: ctx.i18n.t('sats_amount'),
qty: minPaymentAmt,
});
await ctx.wizard.state.updateUI();
return;
}
if (input !== 0 && maxPaymentAmt > 0 && input > maxPaymentAmt) {
ctx.wizard.state.error = ctx.i18n.t('must_be_lt_or_eq', {
fieldName: ctx.i18n.t('sats_amount'),
qty: maxPaymentAmt,
});
await ctx.wizard.state.updateUI();
return;
}
ctx.wizard.state.sats = Math.floor(input);
await ctx.wizard.state.updateUI();
return true;
Expand Down
13 changes: 12 additions & 1 deletion bot/validations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,6 @@ const validateSellOrder = async (ctx: MainContext) => {
return false;
}

// TODO, this validation could be amount > 0?
if (amount !== 0 && amount < Number(process.env.MIN_PAYMENT_AMT)) {
await messages.mustBeGreatherEqThan(
ctx,
Expand All @@ -213,6 +212,12 @@ const validateSellOrder = async (ctx: MainContext) => {
return false;
}

const maxPaymentAmt = Number(process.env.MAX_PAYMENT_AMT) || 0;
if (amount !== 0 && maxPaymentAmt > 0 && amount > maxPaymentAmt) {
await messages.mustBeLessEqThan(ctx, 'monto_en_sats', maxPaymentAmt);
return false;
}

if (fiatAmount.length === 2 && fiatAmount[1] <= fiatAmount[0]) {
await messages.mustBeANumberOrRange(ctx);
return false;
Expand Down Expand Up @@ -301,6 +306,12 @@ const validateBuyOrder = async (ctx: MainContext) => {
return false;
}

const maxPaymentAmt = Number(process.env.MAX_PAYMENT_AMT) || 0;
if (amount !== 0 && maxPaymentAmt > 0 && amount > maxPaymentAmt) {
await messages.mustBeLessEqThan(ctx, 'monto_en_sats', maxPaymentAmt);
return false;
}

if (fiatAmount.length === 2 && fiatAmount[1] <= fiatAmount[0]) {
await messages.mustBeANumberOrRange(ctx);
return false;
Expand Down
1 change: 1 addition & 0 deletions locales/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ help: |
/version - Zeigt die aktuelle Version des Bots
/help - Hilfe
must_be_gt_or_eq: ${fieldName} muss ${qty} oder mehr entsprechen
must_be_lt_or_eq: ${fieldName} muss ${qty} oder weniger entsprechen
you_have_been_banned: Du wurdest gesperrt!
I_told_seller_you_sent_fiat: 🤖 Ich habe @${sellerUsername} gesagt, dass du FIAT-Geld geschickt hast. Wenn der Verkäufer bestätigt, dass er dein Geld erhalten hat, muss er die Mittel freigeben. Falls er sich weigert, kannst du eine Streitigkeit eröffnen.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ help: |
version: Version
commit_hash: Hash of last commit
must_be_gt_or_eq: ${fieldName} Must be greater or equal to ${qty}
must_be_lt_or_eq: ${fieldName} Must be less or equal to ${qty}
you_have_been_banned: You have been banned!
I_told_seller_you_sent_fiat: 🤖 I told @${sellerUsername} that you have sent the fiat money. When the seller confirms that they have received your money, they should release the funds. If they refuse, you can open a dispute.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/es.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ help: |
version: Versión
commit_hash: Hash del último commit
must_be_gt_or_eq: ${fieldName} debe ser mayor o igual que ${qty}
must_be_lt_or_eq: ${fieldName} debe ser menor o igual que ${qty}
you_have_been_banned: ¡Has sido baneado!
I_told_seller_you_sent_fiat: 🤖 Le avisé a @${sellerUsername} que has enviado el dinero fiat, cuando el vendedor confirme que recibió tu dinero deberá liberar los fondos. Si se niega, puedes abrir una disputa.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/fa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ help: |
version: نسخه
commit_hash: هش آخرین پرداخت وثیقه
must_be_gt_or_eq: '${fieldName} باید بزرگ‌تر یا برابر با ${qty} باشد.'
must_be_lt_or_eq: '${fieldName} باید کوچک‌تر یا برابر با ${qty} باشد.'
you_have_been_banned: 'شما محروم شده‌اید!'
I_told_seller_you_sent_fiat: '🤖 من به @${sellerUsername} خبر دادم که شما پول فیات را فرستاده‌اید. فروشنده باید ساتوشی‌ها را پس از بررسی اینکه پول شما را دریافت کرده است، آزاد کند. اگر او این کار را نکرد، می‌توانید یک مشاجره ثبت کنید.'
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/fr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ help: |
/version - Affiche la version actuelle du bot
/help - Messages d'aide
must_be_gt_or_eq: ${fieldName} Doit être supérieur ou égal à ${qty}
must_be_lt_or_eq: ${fieldName} Doit être inférieur ou égal à ${qty}
you_have_been_banned: Tu as été banni !
I_told_seller_you_sent_fiat: "🤖 J'ai dit à @${sellerUsername} que tu as envoyé le paiement fiat. Lorsque le vendeur confirmera avoir reçu ton argent, il devra libérer les fonds. S'il refuse, tu peux ouvrir un litige."
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/it.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ help: |
/version - mostra la versione corrente del bot
/help - messaggi di aiuto
must_be_gt_or_eq: ${fieldName} Deve essere superiore o uguale a ${qty}
must_be_lt_or_eq: ${fieldName} Deve essere inferiore o uguale a ${qty}
you_have_been_banned: Sei stato bannato!
I_told_seller_you_sent_fiat: 🤖 Ho avvisato @${sellerUsername} che hai inviato il denaro fiat, quando il venditore confermerà di aver ricevuto il tuo denaro dovrà liberare i fondi. Se si rifiuta, puoi aprire una disputa.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/ko.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ help: |
/version - 봇의 현재 버전을 보여줍니다.
/help - 도움말을 보여줍니다.
must_be_gt_or_eq: ${fieldName}은 최소 ${qty}보다 크거나 같아야 합니다.
must_be_lt_or_eq: ${fieldName}은 ${qty}보다 작거나 같아야 합니다.
you_have_been_banned: 당신은 추방되었습니다!
I_told_seller_you_sent_fiat: 🤖 @${sellerUsername} 에게 당신이 fiat를 송금했다고 알렸습니다. 판매자가 돈을 받았다고 확인하면 자금을 해제해야 합니다. 만약 거부하면 분쟁을 열 수 있습니다.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/pt.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ help: |
/version - mostra a versão atual do bot
/help - mensagem de ajuda
must_be_gt_or_eq: ${fieldName} Deve ser mais ou igual a ${qty}
must_be_lt_or_eq: ${fieldName} Deve ser menor ou igual a ${qty}
you_have_been_banned: Você foi banido!
I_told_seller_you_sent_fiat: 🤖 Informei a @${sellerUsername} que você enviou o dinheiro fiat, quando o vendedor confirmar que recebeu seu dinheiro, ele deverá liberar os fundos. Se ele se recusar, você pode abrir uma disputa.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/ru.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ help: |
/version - Показывает текущую версию бота
/help - Показать ключевые команды
must_be_gt_or_eq: ${fieldName} должно быть больше или равно ${qty}
must_be_lt_or_eq: ${fieldName} должно быть меньше или равно ${qty}
you_have_been_banned: Вы были забанены!
I_told_seller_you_sent_fiat: 🤖 Я сообщил @${sellerUsername}, что ты отправил фиат, когда продавец подтвердит получение денег, он должен освободить средства. Если он откажется, можешь открыть спор.
buyer_told_me_that_sent_fiat: |
Expand Down
1 change: 1 addition & 0 deletions locales/uk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ help: |
/version - Показує поточну версію бота
/help - Показати ключові команди
must_be_gt_or_eq: ${fieldName} має бути більше чи рівно ${qty}
must_be_lt_or_eq: ${fieldName} має бути менше чи рівно ${qty}
you_have_been_banned: Ви були забанені!
I_told_seller_you_sent_fiat: 🤖 Я повідомив @${sellerUsername}, що ти надіслав фіат, коли продавець підтвердить отримання твоїх грошей, він звільнить кошти. Якщо він відмовиться, ти можеш відкрити спір.
buyer_told_me_that_sent_fiat: |
Expand Down
124 changes: 124 additions & 0 deletions tests/bot/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,71 @@ describe('Validations', () => {
expect(replyStub.calledOnce).to.equal(true);
});

it('should return false if amount exceeds maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['6000', '100', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
expect(result).to.equal(false);
expect(replyStub.calledOnce).to.equal(true);
});

it('should allow amount equal to maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['5000', '100', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
expect(result).to.be.an('object');
});

it('should skip max check when MAX_PAYMENT_AMT is not set', async () => {
ctx.state.command.args = ['10000', '100', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
expect(result).to.be.an('object');
});

it('should allow amount 0 (market price) even with max set', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['0', '100-200', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
expect(result).to.be.an('object');
if (result === false) throw new Error('object expected');
expect(result.amount).to.equal(0);
});

it('should return false if amount is exactly one above maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['5001', '100', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
expect(result).to.equal(false);
});

it('should return object if validation success', async () => {
ctx.state.command.args = ['10000', '100', 'USD', 'zelle'];
const result = await validateSellOrder(ctx);
Expand Down Expand Up @@ -217,6 +282,65 @@ describe('Validations', () => {
expect(replyStub.calledOnce).to.be.equal(true);
});

it('should return false if amount exceeds maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['6000', '100', 'USD', 'zelle'];
const result = await validateBuyOrder(ctx);
expect(result).to.equal(false);
expect(replyStub.calledOnce).to.equal(true);
});

it('should allow amount equal to maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['5000', '100', 'USD', 'zelle'];
const result = await validateBuyOrder(ctx);
expect(result).to.be.an('object');
});

it('should allow amount 0 (market price) even with max set', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['0', '100-200', 'USD', 'zelle'];
const result = await validateBuyOrder(ctx);
expect(result).to.be.an('object');
if (result === false) throw new Error('object expected');
expect(result.amount).to.equal(0);
});

it('should return false if amount is exactly one above maximum', async () => {
sandbox.restore();
sandbox = sinon.createSandbox();
sandbox.stub(process, 'env').value({
MIN_PAYMENT_AMT: 100,
MAX_PAYMENT_AMT: 5000,
NODE_ENV: 'production',
INVOICE_EXPIRATION_WINDOW: 3600000,
});
ctx.state.command.args = ['5001', '100', 'USD', 'zelle'];
const result = await validateBuyOrder(ctx);
expect(result).to.equal(false);
});

it('should return object if validation success', async () => {
ctx.state.command.args = ['10000', '100', 'USD', 'zelle'];
const result = await validateBuyOrder(ctx);
Expand Down