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
11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ This program implements a payment distribution system where payments can be spli

### Key Features

- Token whitelist management
- Payment execution with broadcaster revenue sharing (70/30 split)
- Payment execution with a 2% treasury cut; when a broadcaster co-signs, 30% of that cut goes to the broadcaster and 70% to treasury (see `constants.rs`)
- Arcium encrypted computation integration
- Payment statistics tracking via confidential computations

Expand Down Expand Up @@ -64,12 +63,6 @@ arcium deploy \
npx ts-node init-comp-def-final.ts
```

### 4. Whitelist tokens

```bash
npx ts-node scripts/whitelist-tokens.ts
```

## Testing

```bash
Expand Down Expand Up @@ -105,7 +98,7 @@ yarn test

## Program IDs

- Program ID: `7fvHNYVuZP6EYt68GLUa4kU8f8dCBSaGafL9aDhhtMZN`
- Program ID: `7xeQNUggKc2e5q6AQxsFBLBkXGg2p54kSx11zVainMks` (matches `Anchor.toml` / `declare_id!` in `programs/ble-revshare`)
- Arcium Program ID: `Arcj82pX7HxYKLR92qvgZUAd7vGS1k4hQvAFcPATFdEQ`

## License
Expand Down
3 changes: 3 additions & 0 deletions programs/ble-revshare/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ use arcium_anchor::prelude::comp_def_offset;

pub const COMP_DEF_OFFSET_PAYMENT_STATS: u32 = comp_def_offset("payment_v3");

pub const TREASURY_CUT_BPS: u64 = 2;
pub const BROADCASTER_SHARE_OF_TREASURY_BPS: u64 = 30;

pub const TREASURY_WALLET: Pubkey = pubkey!("DqyfDvr7yG4d3mtW6AiXgbuVM7GZWqn4RVARFbJxwtFc");
2 changes: 2 additions & 0 deletions programs/ble-revshare/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ pub enum ErrorCode {
InvalidTreasury,
#[msg("Payment payload has expired")]
PaymentExpired,
#[msg("Payment amount must be greater than zero")]
InvalidAmount,
}
9 changes: 6 additions & 3 deletions programs/ble-revshare/src/instructions/execute_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use arcium_anchor::prelude::*;

use super::payment_callback::PaymentV3Callback;
use crate::{ArciumSignerAccount, ID, ID_CONST};
use crate::constants::{COMP_DEF_OFFSET_PAYMENT_STATS, TREASURY_WALLET};
use crate::constants::{
BROADCASTER_SHARE_OF_TREASURY_BPS, COMP_DEF_OFFSET_PAYMENT_STATS, TREASURY_CUT_BPS, TREASURY_WALLET,
};
use crate::errors::ErrorCode;
use crate::events::PaymentEvent;
use crate::state::PaymentReceipt;
Expand Down Expand Up @@ -115,6 +117,7 @@ pub(crate) fn handler(
Clock::get()?.unix_timestamp <= expires_at,
ErrorCode::PaymentExpired
);
require!(amount > 0, ErrorCode::InvalidAmount);

msg!("Payer: {}", ctx.accounts.payer.key());
msg!("Sign PDA: {}", ctx.accounts.sign_pda_account.key());
Expand Down Expand Up @@ -146,7 +149,7 @@ pub(crate) fn handler(
);

let treasury_total_amount = amount
.checked_mul(2)
.checked_mul(TREASURY_CUT_BPS)
.ok_or(ErrorCode::MathOverflow)?
/ 100;

Expand All @@ -173,7 +176,7 @@ pub(crate) fn handler(
);

broadcaster_share_amount = treasury_total_amount
.checked_mul(30)
.checked_mul(BROADCASTER_SHARE_OF_TREASURY_BPS)
.ok_or(ErrorCode::MathOverflow)?
/ 100;
} else if ctx.accounts.broadcaster_token_account.is_some() {
Expand Down
39 changes: 39 additions & 0 deletions tests/ble-revshare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,45 @@ describe("ble-revshare", () => {
}
});

test("rejects zero payment amount", async () => {
const computationOffset = new anchor.BN(randomBytes(8), "hex");
const nonce = BigInt(deserializeLE(randomBytes(16)).toString());
const paymentId = Buffer.from(randomBytes(32));
const expiresAt = new anchor.BN(Math.floor(Date.now() / 1000) + PAYLOAD_TTL_SECS);

try {
await program.methods
.executePayment(
computationOffset,
Array.from(paymentId),
new anchor.BN(0),
Array.from(randomBytes(32)),
new anchor.BN(nonce.toString()),
Array.from(payer.publicKey.toBytes()),
expiresAt
)
.accountsPartial({
payer: payer.publicKey,
broadcaster: broadcaster.publicKey,
recipient: recipient.publicKey,
mint,
paymentReceipt: paymentReceipt(paymentId),
payerTokenAccount: senderTokenAccount,
recipientTokenAccount,
treasuryTokenAccount,
broadcasterTokenAccount,
...arciumAccounts(computationOffset),
})
.signers([payer, broadcaster])
.rpc();

assert.fail("Expected InvalidAmount error");
} catch (err) {
const error = ensureError(err);
assert.match(error.message, /InvalidAmount/);
}
});

test("rejects expired payment payload", async () => {
const computationOffset = new anchor.BN(randomBytes(8), "hex");
const nonce = BigInt(deserializeLE(randomBytes(16)).toString());
Expand Down