-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Implement WrappedTestnetBTC contract #1473
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
base: main
Are you sure you want to change the base?
Conversation
Added WrappedTestnetBTC contract with minting and burning functionality.
DOUGLASDAVIS08161978
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
uses: actions/setup-node@v6
with:Version Spec of the version to use in SemVer notation.
It also admits such aliases as lts/*, latest, nightly and canary builds
Examples: 12.x, 10.15.1, >=10.15.0, lts/Hydrogen, 16-nightly, latest, node
node-version: ''
File containing the version Spec of the version to use. Examples: package.json, .nvmrc, .node-version, .tool-versions.
If node-version and node-version-file are both provided the action will use version from node-version.
node-version-file: ''
Set this option if you want the action to check for the latest available version
that satisfies the version spec.
It will only get affect for lts Nodejs versions (12.x, >=10.15.0, lts/Hydrogen).
Default: false
check-latest: false
Target architecture for Node to use. Examples: x86, x64. Will use system architecture by default.
Default: ''. The action use system architecture by default
architecture: ''
Used to pull node distributions from https://github.com/actions/node-versions.
Since there's a default, this is typically not supplied by the user.
When running this action on github.com, the default value is sufficient.
When running on GHES, you can pass a personal access token for github.com if you are experiencing rate limiting.
We recommend using a service account with the least permissions necessary. Also
when generating a new PAT, select the least scopes necessary.
Learn more about creating and using encrypted secrets
Default: ${{ github.server_url == 'https://github.com' && github.token || '' }}
token: ''
Used to specify a package manager for caching in the default directory. Supported values: npm, yarn, pnpm.
Package manager should be pre-installed
Default: ''
cache: ''
Controls automatic caching for npm. By default, caching for npm is enabled if either the devEngines.packageManager field or the top-level packageManager field in package.json specifies npm and no explicit cache input is provided.
To disable automatic caching for npm, set package-manager-cache to false.
default: true
package-manager-cache: true
Used to specify the path to a dependency file: package-lock.json, yarn.lock, etc.
It will generate hash from the target file for primary key. It works only If cache is specified.
Supports wildcards or a list of file names for caching multiple dependencies.
Default: ''
cache-dependency-path: ''
Optional registry to set up for auth. Will set the registry in a project level .npmrc and .yarnrc file,
and set up auth to read in from env.NODE_AUTH_TOKEN.
Default: ''
registry-url: ''
Optional scope for authenticating against scoped registries.
Will fall back to the repository owner when using the GitHub Packages registry (https://npm.pkg.github.com/).
Default: ''
scope: ''
Optional mirror to download binaries from.
Artifacts need to match the official Node.js
Example:
V8 Canary Build: <mirror_url>/download/v8-canary
RC Build: <mirror_url>/download/rc
Official: Build <mirror_url>/dist
Nightly build: <mirror_url>/download/nightly
Default: ''
mirror: ''
Optional mirror token.
The token will be used as a bearer token in the Authorization header
Default: ''
mirror-token: '' && import http.server
import socketserver
import json
import os
PORT = 8000
FILE_NAME = "nexus_seeds.json"
class NexusHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Serve the JSON file regardless of the path for this demo
# This mimics the /.well-known/seeds-public.json endpoint
if self.path.endswith(".json") or self.path == "/":
if os.path.exists(FILE_NAME):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
with open(FILE_NAME, 'rb') as f:
self.wfile.write(f.read())
print(f"⚡ [SERVER] Served {FILE_NAME} to Nexus Client")
else:
self.send_error(404, "Seeds file not found")
else:
self.send_error(404, "Endpoint not found")
print(f"🌍 NEXUS AGI DIRECTORY ONLINE")
print(f"📡 Listening on http://localhost:{PORT}")
print(f"📂 Serving registry from: {FILE_NAME}")
with socketserver.TCPServer(("", PORT), NexusHandler) as httpd:
httpd.serve_forever()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR attempts to add a Solidity smart contract (WrappedTestnetBTC) to a Node.js GitHub Action repository by appending it to the action.yml file. This is fundamentally incorrect as the repository is a GitHub Action for setting up Node.js environments, not a Solidity/blockchain project.
Changes:
- Appended a complete Solidity smart contract to the action.yml configuration file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| main: 'dist/setup/index.js' | ||
| post: 'dist/cache-save/index.js' | ||
| post-if: success() | ||
| && // SPDX-License-Identifier: MIT |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solidity contract code is incorrectly added to a YAML file (action.yml). This file is a GitHub Action configuration for Node.js setup, not a Solidity contract repository. The contract should be placed in a separate .sol file in an appropriate directory (e.g., contracts/WrappedTestnetBTC.sol), and this entire addition to action.yml should be removed.
| main: 'dist/setup/index.js' | ||
| post: 'dist/cache-save/index.js' | ||
| post-if: success() | ||
| && // SPDX-License-Identifier: MIT |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 44 starts with '&&' which is invalid syntax for both YAML and Solidity. This will cause parsing errors when the action.yml file is processed by GitHub Actions.
| whenNotPaused | ||
| { | ||
| require(to != address(0), "Mint to zero address"); | ||
| require(amount > 0, "Amount must be positive"); |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mint function allows minting zero tokens to pass the check on line 87, but then still emits events and updates state. While line 87 checks for 'amount > 0', an amount of 0 would still fail this check. However, the broader issue is this entire contract should not be in action.yml at all.
| balanceOf[to] += amount; | ||
| totalSupply += amount; |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mint function lacks protection against integer overflow in Solidity 0.8.20, though Solidity 0.8+ has built-in overflow protection. However, there's no maximum supply cap, which could allow unlimited minting by the bridge operator. Consider adding a max supply limit for security.
| external | ||
| whenNotPaused | ||
| returns (bool) | ||
| { |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The approve function is vulnerable to the ERC20 approval race condition. When changing an approval from a non-zero value to another non-zero value, an attacker could spend both the old and new amounts. Consider implementing increaseAllowance/decreaseAllowance functions or requiring the allowance to be set to zero first.
| { | |
| { | |
| require(spender != address(0), "Approve to zero address"); | |
| // Mitigate ERC20 approval race condition by enforcing zero-first updates | |
| require( | |
| amount == 0 || allowance[msg.sender][spender] == 0, | |
| "Non-zero to non-zero allowance change not allowed" | |
| ); |
|
|
||
| function burn(uint256 amount, string calldata bitcoinAddress) | ||
| external | ||
| whenNotPaused | ||
| { | ||
| require(balanceOf[msg.sender] >= amount, "Insufficient balance"); | ||
| require(bytes(bitcoinAddress).length > 0, "Bitcoin address required"); |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The burn function only validates that bitcoinAddress is non-empty but doesn't validate its format or correctness. This could lead to tokens being burned with invalid Bitcoin addresses, resulting in permanent loss of funds. Consider adding proper Bitcoin address format validation.
| function burn(uint256 amount, string calldata bitcoinAddress) | |
| external | |
| whenNotPaused | |
| { | |
| require(balanceOf[msg.sender] >= amount, "Insufficient balance"); | |
| require(bytes(bitcoinAddress).length > 0, "Bitcoin address required"); | |
| function isValidBitcoinAddress(string memory bitcoinAddress) internal pure returns (bool) { | |
| bytes memory addrBytes = bytes(bitcoinAddress); | |
| uint256 len = addrBytes.length; | |
| // Typical Bitcoin address lengths (legacy, P2SH, Bech32) | |
| if (len < 26 || len > 62) { | |
| return false; | |
| } | |
| // Check common mainnet and testnet prefixes: | |
| // '1', '3', 'm', 'n', '2', 'bc1', 'tb1' | |
| bool hasValidPrefix = false; | |
| if ( | |
| addrBytes[0] == bytes1("1") || | |
| addrBytes[0] == bytes1("3") || | |
| addrBytes[0] == bytes1("m") || | |
| addrBytes[0] == bytes1("n") || | |
| addrBytes[0] == bytes1("2") | |
| ) { | |
| hasValidPrefix = true; | |
| } else if (len >= 3) { | |
| if ( | |
| addrBytes[0] == bytes1("b") && | |
| addrBytes[1] == bytes1("c") && | |
| addrBytes[2] == bytes1("1") | |
| ) { | |
| hasValidPrefix = true; | |
| } else if ( | |
| addrBytes[0] == bytes1("t") && | |
| addrBytes[1] == bytes1("b") && | |
| addrBytes[2] == bytes1("1") | |
| ) { | |
| hasValidPrefix = true; | |
| } | |
| } | |
| if (!hasValidPrefix) { | |
| return false; | |
| } | |
| // Ensure all characters are alphanumeric (reject spaces and symbols) | |
| for (uint256 i = 0; i < len; i++) { | |
| bytes1 c = addrBytes[i]; | |
| bool isDigit = (c >= bytes1("0") && c <= bytes1("9")); | |
| bool isUpper = (c >= bytes1("A") && c <= bytes1("Z")); | |
| bool isLower = (c >= bytes1("a") && c <= bytes1("z")); | |
| if (!(isDigit || isUpper || isLower)) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function burn(uint256 amount, string calldata bitcoinAddress) | |
| external | |
| whenNotPaused | |
| { | |
| require(balanceOf[msg.sender] >= amount, "Insufficient balance"); | |
| require(isValidBitcoinAddress(bitcoinAddress), "Invalid Bitcoin address"); |
| function changeBridgeOperator(address newOperator) external onlyBridgeOperator { | ||
| require(newOperator != address(0), "Zero address"); | ||
| emit BridgeOperatorChanged(bridgeOperator, newOperator); | ||
| bridgeOperator = newOperator; | ||
| } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changeBridgeOperator function lacks a two-step transfer mechanism. If the newOperator address is incorrect or compromised, control of the contract cannot be recovered. Consider implementing a two-step transfer process with acceptance by the new operator.
| function changeBridgeOperator(address newOperator) external onlyBridgeOperator { | |
| require(newOperator != address(0), "Zero address"); | |
| emit BridgeOperatorChanged(bridgeOperator, newOperator); | |
| bridgeOperator = newOperator; | |
| } | |
| address public pendingBridgeOperator; | |
| event BridgeOperatorChangeInitiated(address indexed currentOperator, address indexed newOperator); | |
| modifier onlyPendingBridgeOperator() { | |
| require(msg.sender == pendingBridgeOperator, "Not pending operator"); | |
| _; | |
| } | |
| function changeBridgeOperator(address newOperator) external onlyBridgeOperator { | |
| require(newOperator != address(0), "Zero address"); | |
| pendingBridgeOperator = newOperator; | |
| emit BridgeOperatorChangeInitiated(bridgeOperator, newOperator); | |
| } | |
| function acceptBridgeOperator() external onlyPendingBridgeOperator { | |
| emit BridgeOperatorChanged(bridgeOperator, pendingBridgeOperator); | |
| bridgeOperator = pendingBridgeOperator; | |
| pendingBridgeOperator = address(0); | |
| } |
@claude Added WrappedTestnetBTC contract with minting and burning functionality.
Description:
Describe your changes.
Related issue:
Add link to the related issue.
Check list: