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
20 changes: 20 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
node_modules
**/node_modules
npm-debug.log*
frontend/dist
frontend/build
frontend/.env
frontend/.env.local
contracts/out
contracts/cache
contracts/broadcast
contracts/.env
.git
.gitignore
.gitmodules
*.md
docs/
.vscode
.idea
.DS_Store
Thumbs.db
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "contracts/lib/chainlink-brownie-contracts"]
path = contracts/lib/chainlink-brownie-contracts
url = https://github.com/smartcontractkit/chainlink-brownie-contracts
[submodule "contracts/lib/openzeppelin-contracts"]
path = contracts/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
Comment on lines +7 to +9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the full .gitmodules file to see if commit is pinned
echo "=== Full .gitmodules content ==="
cat .gitmodules

echo -e "\n=== Checking for foundry.toml ==="
find . -name "foundry.toml" -type f | head -10

echo -e "\n=== Checking if submodule directory exists ==="
ls -la contracts/lib/openzeppelin-contracts 2>/dev/null | head -20 || echo "Submodule not found/initialized"

echo -e "\n=== Checking .git/config for submodule details ==="
grep -A 3 "openzeppelin" .git/config 2>/dev/null || echo "Not in .git/config"

echo -e "\n=== Checking .gitmodules for submodule ref ==="
cat .gitmodules | grep -A 5 "openzeppelin"

Repository: StabilityNexus/Chainvoice

Length of output: 1045


🏁 Script executed:

#!/bin/bash
# Check foundry.toml content
echo "=== foundry.toml content ==="
cat contracts/foundry.toml

echo -e "\n=== Checking .git/modules for submodule details ==="
if [ -d .git/modules/contracts/lib/openzeppelin-contracts ]; then
  echo "Submodule .git directory exists"
  if [ -f .git/modules/contracts/lib/openzeppelin-contracts/HEAD ]; then
    echo "HEAD file content:"
    cat .git/modules/contracts/lib/openzeppelin-contracts/HEAD
  fi
fi

echo -e "\n=== Check git ls-files for .gitmodules entry details ==="
git ls-files --stage | grep gitmodules
</parameter>

Repository: StabilityNexus/Chainvoice

Length of output: 858


🏁 Script executed:

#!/bin/bash
# Find all foundry.toml files
echo "=== All foundry.toml files ==="
find . -name "foundry.toml" -type f

echo -e "\n=== Directory structure of contracts/lib ==="
ls -la contracts/lib/

echo -e "\n=== Check if lib directory exists at root ==="
ls -la lib/ 2>/dev/null || echo "No lib directory at root"

echo -e "\n=== Verify foundry.toml working directory context ==="
pwd
cat -n contracts/foundry.toml

Repository: StabilityNexus/Chainvoice

Length of output: 1074


Submodule must be pinned to a specific commit or stable release tag.

The OpenZeppelin Contracts submodule in .gitmodules is not pinned to any specific commit or release tag. Currently, it will point to the main branch, which is unsuitable for production use and breaks build reproducibility.

Update .gitmodules to include a pinned commit:

Example fix
[submodule "contracts/lib/openzeppelin-contracts"]
	path = contracts/lib/openzeppelin-contracts
	url = https://github.com/OpenZeppelin/openzeppelin-contracts
	branch = v5.0.0

Or set a specific commit in git after initialization. Verify with a stable release tag (e.g., v5.0.0) rather than main.

The foundry remapping in contracts/foundry.toml is correctly configured as '@openzeppelin/=lib/openzeppelin-contracts/'.

🤖 Prompt for AI Agents
In @.gitmodules around lines 7 - 9, The .gitmodules entry for the OpenZeppelin
submodule is not pinned and currently tracks the default branch; update the
submodule configuration for "contracts/lib/openzeppelin-contracts" to reference
a stable release tag or branch (e.g., set branch = v5.0.0) so the submodule is
deterministic, then commit the updated .gitmodules; alternatively, after cloning
ensure the submodule is checked out to a specific commit/tag using git submodule
update --init --remote --checkout <path> and record that pinned commit in the
repo state so builds are reproducible.

1 change: 0 additions & 1 deletion contracts/.env.example

This file was deleted.

11 changes: 11 additions & 0 deletions contracts/foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"lib/forge-std": {
"rev": "3b20d60d14b343ee4f908cb8079495c07f5e8981"
},
"lib\\openzeppelin-contracts": {
"tag": {
"name": "v5.5.0",
"rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565"
}
}
}
3 changes: 2 additions & 1 deletion contracts/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
src = "src"
out = "out"
libs = ["lib"]
solc_version = '0.8.13'
solc_version = '0.8.20'
remappings = ['@openzeppelin/=lib/openzeppelin-contracts/']
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
# forge create --rpc-url https://eth-sepolia.g.alchemy.com/v2/nnY0qPUQLYsUvb5BKJM5bh81sI6O0PQG --private-key fba7342ef6879df2c735644c734ea69c140f423d84eb2d53fbdfd53fd5d7c586 src/Token.sol:MyToken --legacy
1 change: 1 addition & 0 deletions contracts/lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at fcbae5
28 changes: 8 additions & 20 deletions contracts/src/Chainvoice.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;
pragma solidity ^0.8.20;


import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IERC20 {
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
}

contract Chainvoice {
using SafeERC20 for IERC20;
// Errors
error MixedTokenBatch();
error InvalidBatchSize();
Expand Down Expand Up @@ -70,16 +70,6 @@ contract Chainvoice {
// Constants
uint256 public constant MAX_BATCH = 50;

// Internal utils
function _isERC20(address token) internal view returns (bool) {
if (token == address(0)) return false;
if (token.code.length == 0) return false;
(bool success, ) = token.staticcall(
abi.encodeWithSignature("balanceOf(address)", address(this))
);
return success;
}

// ========== Single-invoice create ==========
function createInvoice(
address to,
Expand Down Expand Up @@ -226,12 +216,11 @@ contract Chainvoice {

accumulatedFees += fee;

bool transferSuccess = IERC20(invoice.tokenAddress).transferFrom(
IERC20(invoice.tokenAddress).safeTransferFrom(
msg.sender,
invoice.from,
invoice.amountDue
);
require(transferSuccess, "Token transfer failed");
}

emit InvoicePaid(
Expand Down Expand Up @@ -304,8 +293,7 @@ contract Chainvoice {

for (uint256 i = 0; i < n; i++) {
InvoiceDetails storage inv = invoices[invoiceIds[i]];
bool ok = erc20.transferFrom(msg.sender, inv.from, inv.amountDue);
require(ok, "Token transfer failed");
erc20.safeTransferFrom(msg.sender, inv.from, inv.amountDue);
emit InvoicePaid(inv.id, inv.from, inv.to, inv.amountDue, token);
}
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/Chainvoice.t.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";
Expand Down
32 changes: 32 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: '3.8'

services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: chainvoice-frontend
ports:
- "5173:5173"
volumes:
- ./frontend:/app
- /app/node_modules
env_file:
- ./frontend/.env
restart: unless-stopped
networks:
- chainvoice-network

blockchain:
image: ghcr.io/foundry-rs/foundry:latest
container_name: chainvoice-blockchain
command: anvil --host 0.0.0.0 --chain-id 31337 --accounts 10 --balance 10000
ports:
- "8545:8545"
restart: unless-stopped
networks:
- chainvoice-network

networks:
chainvoice-network:
driver: bridge
30 changes: 30 additions & 0 deletions frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
dist
build
.next
out
.env
.env.local
.env.*.local
.git
.gitignore
.gitattributes
.vscode
.idea
*.swp
*.swo
*~
.DS_Store
Thumbs.db
coverage
.nyc_output
*.test.js
*.spec.js
README.md
*.md
Dockerfile
docker-compose.yml
.dockerignore
13 changes: 13 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:20-alpine

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm ci --legacy-peer-deps

COPY . .

EXPOSE 5173

CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
12 changes: 12 additions & 0 deletions frontend/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,17 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"),
},
},
server: {
host: '0.0.0.0',
port: 5173,
strictPort: true,
watch: {
usePolling: true,
},
hmr: {
host: 'localhost',
port: 5173,
},
},
})