Skip to content

Commit

Permalink
Merge pull request #66 from ferrumnet/develop
Browse files Browse the repository at this point in the history
Release - Testnet
  • Loading branch information
naiemk authored Sep 24, 2024
2 parents 5b6b43c + 059288f commit 990050a
Show file tree
Hide file tree
Showing 159 changed files with 13,264 additions and 6,095 deletions.
85 changes: 42 additions & 43 deletions QpDeployConfig.yaml
Original file line number Diff line number Diff line change
@@ -1,48 +1,47 @@
#####
#
# This config file is used by the QP deploy script.
# First, remove the hardcoded contract address for the contract you want to be deployed.
# Then, after deployment, put the deployed addresses, in the config and send a commit
# tagged with UPDATE_QP_CONFIG
#
# Use $in the DeployerKeys section to get the value from an environment variable
# Note: If you provide `DeployerKeys.Owner`, you do NOT need to provide the `Owner`
#
#####

Owner: "0xf1b1145f83cadd5199f8D8E937Aa07979e2806Aa"
DeployerContract: "0xDACa19A67eB89D610eDFd891594108B569c379c0"
DeployerSalt: "0x65ff7961289d09280b311966c72b708989daabefb729d1a5b64fe72a9ad26049"
QuantumPortalGateway:
QuantumPortalState:
QuantumPortalPoc:
QuantumPortalLedgerMgr:
QuantumPortalAuthorityMgr: "0xe776Fedb74e85CFe6677b6774E5898c1d41867D7"
QuantumPortalFeeConvertorDirect: "0x431D332F39A90e4478Ae3a13C8e17Bae1BfdDC73"
QuantumPortalMinerMgr: "0x391870f7FFEC11582f3d1Dc6bc0EE7ec8e63C16f"
QuantumPortalStake: "0xB1CcE8e7039395348283cbb267007351a7777876"
QuantumPortalMinStake?: "10000000000000000000"
Owner: ''
DeployerContract: ''
DeployerSalt: ''
QuantumPortalGateway: '0x783eaa3d0faC7e1e335705A0554cD98E15e72dFD'
QuantumPortalPoc: '0x968D9e7De42b224a67E131EE26893Ef3dC846A1e'
QuantumPortalLedgerMgr: '0xfe3031004aDB3b621Fe20132e435ff6f491234DB'
QuantumPortalAuthorityMgr: '0xB4C97f762BCcd93632346738Ea09157A91d8cA91'
QuantumPortalFeeConvertorDirect: '0xa3753397B83725Fca56719Af9E7527a098021878'
QuantumPortalMinerMgr: '0xbf18631e92A21b1DD4C6Cc49CF6A0d500A41f74A'
QuantumPortalStake: '0x2E37B6D588c105f9b12ca5C2bfED009582dd3341'
QuantumPortalNativeFeeRepo: '0x7A2C78415267d1125d7d05402d743f9ae675F6a7'
QuantumPortalMinStake: '10000000000000000000'
FRM:
97: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C"
43113: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C"
80001: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C"
26100: "0x0000000000000000000000000000000000026026"
81: "0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12"
WFRM: "0x0000000000000000000000000000000000026026"
'56': '0x0000000000000000000000000000000000026026'
'81': '0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12'
'97': '0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C'
'26100': '0x71156ADcddCA1DB1283EA5AA6436c5a1639b1FA2'
'31337': '0xd12e9329865B6423E39a2E4C3d58b7a1C52f3849'
'42161': '0x6D34420DcAf516bEc9D81e5d79FAC2100058C9AC'
'43113': '0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C'
'80001': '0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C'
WFRM: '0x0000000000000000000000000000000000026026'
WETH:
97: "0x0000000000000000000000000000000000026026"
43113: "0x0000000000000000000000000000000000026026"
80001: "0x0000000000000000000000000000000000026026"
26026: "0x0000000000000000000000000000000000026026"
81: "0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12"
'56': '0x0000000000000000000000000000000000026026'
'81': '0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12'
'97': '0x0000000000000000000000000000000000026026'
'26026': '0x0000000000000000000000000000000000026026'
'26100': '0x0000000000000000000000000000000000026026'
'31337': '0x0000000000000000000000000000000000026026'
'43113': '0x0000000000000000000000000000000000026026'
'80001': '0x0000000000000000000000000000000000026026'
UniV2Factory:
97: "0x0000000000000000000000000000000000026026"
43113: "0x0000000000000000000000000000000000026026"
80001: "0x0000000000000000000000000000000000026026"
26026: "0x0000000000000000000000000000000000026026"
'56': ''
'97': '0x0000000000000000000000000000000000026026'
'26026': '0x0000000000000000000000000000000000026026'
'31337': '0x0000000000000000000000000000000000026026'
'43113': '0x0000000000000000000000000000000000026026'
'80001': '0x0000000000000000000000000000000000026026'
DirectFee:
feePerByte: "10000000000000"
feePerByte: '10000000000000'
DeployerKeys:
DeployerContract: $CONTRACT_DEPLOYER_KEY
Qp: $QP_DEPLOYER_KEY
Owner: $QP_DEPLOYER_KEY
DeployerContract:
type: secret
env: PAIVATE_KEY_SECRET_ARN
value: PRIVATEKEY_TEST_VALIDATOR
Qp: c51a3cddd45c4be158d9ca4450402ee96c34639b50ed990964d0990d8736c6b7
Owner: c51a3cddd45c4be158d9ca4450402ee96c34639b50ed990964d0990d8736c6b7
125 changes: 125 additions & 0 deletions bin/generateSource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import os
import json
import json5
import re
import argparse

def read_file(file_path):
with open(file_path, 'r') as file:
return file.read()

def apply_remappings(import_path, remappings):
for prefix, target in remappings:
if import_path.startswith(prefix):
return import_path.replace(prefix, target, 1)
return import_path

def get_imports(file_content):
import_pattern = re.compile(r'import\s+(?:(?:["\']([^"\']+)["\'])|(?:{[^}]*}\s+from\s+["\']([^"\']+)["\']));')
matches = import_pattern.findall(file_content)
# Extract the import paths from the matches
imports = [match[0] if match[0] else match[1] for match in matches]
return imports

def get_all_dependencies(contract_path, main_contract, remappings):
def recursive_find_dependencies(file_path, sources, visited):
if file_path in visited:
return
visited.add(file_path)
file_content = read_file(file_path)
cwd = os.path.abspath(".") + "/"
clean_path = file_path.replace(cwd, "")
clean_path = clean_path.replace("node_modules/", "")
sources[clean_path] = {"content": file_content}
imports = get_imports(file_content)
print("IMPORTS", clean_path, imports)
for import_path in imports:
mapped_import_path = apply_remappings(import_path, remappings)
absolute_import_path = os.path.abspath(os.path.normpath(os.path.join(os.path.dirname(file_path), mapped_import_path)))
if os.path.exists(absolute_import_path):
print("imported ", import_path)
recursive_find_dependencies(absolute_import_path, sources, visited)
else:
print(f"Warning: Import {import_path} not found.", absolute_import_path)

sources = {}
visited = set()
main_contract_path = os.path.join(contract_path, main_contract)
recursive_find_dependencies(main_contract_path, sources, visited)
return sources

def get_hardhat_config(config_path):
with open(config_path, 'r') as f:
return json5.load(f)

def parse_remappings(remappings):
parsed_remappings = []
for remapping in remappings:
prefix, target = remapping.split('=')
parsed_remappings.append((prefix, os.path.abspath(target) + "/"))
return parsed_remappings

def generate_standard_input_json(contract_path, config_path, main_contract):
hardhat_config = get_hardhat_config(config_path)
remappings = parse_remappings(hardhat_config.get('remappings', []))
sources = get_all_dependencies(contract_path, main_contract, remappings)
hardhat_config = get_hardhat_config(config_path)

optimizer_settings = hardhat_config['solidity']['settings']['optimizer']
evm_version = hardhat_config['solidity']['settings'].get('evmVersion', 'istanbul')

libraries = {}
if 'libraries' in hardhat_config['solidity']['settings']:
libraries = hardhat_config['solidity']['settings']['libraries']

remappings = []
if 'remappings' in hardhat_config:
remappings = hardhat_config['remappings']

standard_input_json = {
"language": "Solidity",
"sources": sources,
"settings": {
"optimizer": optimizer_settings,
"evmVersion": evm_version,
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
],
"": [
"ast"
]
}
},
"libraries": libraries,
#"remappings": remappings
}
}

return standard_input_json

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate Solidity Standard Input JSON for a specific contract file.")
parser.add_argument("contract_file", help="The main Solidity contract file to verify, e.g., MyContract.sol")
args = parser.parse_args()

contract_path = "contracts" # Path to your contracts directory
config_path = "hardhat.config.json" # Path to your Hardhat config file
main_contract = args.contract_file # Main contract file to verify

standard_input_json = generate_standard_input_json(contract_path, config_path, main_contract)

output_dir = os.path.join("artifacts", "standard_input", os.path.splitext(main_contract)[0])
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, "standard_input.json")

with open(output_path, 'w') as f:
json.dump(standard_input_json, f, indent=4)

print(f"Standard input JSON written to {output_path}")


2 changes: 2 additions & 0 deletions bin/node.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
#!/bin/bash

echo "npx hardhat node --fork https://eth-mainnet.alchemyapi.io/v2/$ALCHEMY_API_KEY"
npx hardhat node --fork https://eth-mainnet.alchemyapi.io/v2/$ALCHEMY_API_KEY
12 changes: 12 additions & 0 deletions contracts/btfd/BatchCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.8.24;

error CallFailed(address target, bytes call, bytes err);

contract BatchCall {
function batchCall(address target, bytes[] calldata calls) external {
for(uint i=0; i<calls.length; i++) {
(bool result, bytes memory err) = target.call(calls[i]);
if (!result) revert CallFailed(target, calls[i], err);
}
}
}
66 changes: 66 additions & 0 deletions contracts/btfd/Bitcoin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
pragma solidity ^0.8.24;

import "./QpErc20Token.sol";
import "./IFeeStore.sol";

error FeeIsLessThanAmountSentToQp(uint amount, uint fee);

contract Bitcoin is QpErc20Token {
uint constant SATOSHI_TO_NAKAMOTO_CONVERSION = 1 gwei;
function initialize(
) external initializer {
__QPERC20_init(0, 0, "Bitcoin", "BTC", 18, 0);
__TokenReceivable_init();
__Context_init();
}

/**
* @notice Procecess the fee and updates the amount if necessary
*/
function collectFeeForFutureUse(bytes32 txId, uint feeInBtc) internal returns (uint) {
QpErc20Storage storage $ = _getQPERC20Storage();
address feeStore = $.factory.feeStore();
_mintQp(feeStore, feeInBtc);
IFeeStore(feeStore).swapBtcWithFee(txId, feeInBtc);
}

/**
* @notice Procecess the fee and updates the amount if necessary
*/
function processFee(bytes32 txId, uint amount, uint feeInBtc) internal override returns (uint) {
QpErc20Storage storage $ = _getQPERC20Storage();
feeInBtc *= SATOSHI_TO_NAKAMOTO_CONVERSION; // Fee is in sats. We convert it to nakamoto
if (amount < feeInBtc) {
revert FeeIsLessThanAmountSentToQp(amount, feeInBtc);
}
collectFeeForFutureUse(txId, feeInBtc);
$.factory.feeStoreCollectFee(txId); // Merge the two steps, where in case of rune, these two steps are managed in separate mining calls
return amount - feeInBtc;
}

function preProcessValues(uint[] memory values) internal pure override returns (uint[] memory) {
for (uint i=0; i<values.length; i++) {
values[i] *= SATOSHI_TO_NAKAMOTO_CONVERSION;
}
return values;
}

function processRemoteCall(bytes32 txId, bytes memory remoteCall, uint amount) internal override {
if (remoteCall.length == 0) {
// Just collect the fee
collectFeeForFutureUse(txId, amount);
} else {
QpErc20Token.processRemoteCall(txId, remoteCall, amount);
}
}

/**
* @notice Collects fee for BTC. Difference is that the fee can be taked without allowance
*/
function collectSettlementFee(uint feeToCollect) internal override returns (uint) {
if (feeToCollect != 0) {
_transferQp(_msgSender(), address(this), feeToCollect);
}
return sync(address(this));
}
}
42 changes: 42 additions & 0 deletions contracts/btfd/BtcLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
pragma solidity ^0.8.24;

library BtcLib {
struct TransferItem {
address addr;
uint value;
}

function parseBtcAddress(string calldata btcAddress) internal view returns (address) {
// TODO: Call pre-compile
}

function initiateWithdrawal(string calldata btcAddress, uint tokenId, uint version, uint btcFee, bytes32 settlementId) internal {
// TODO: Call pre-compile
// Note. Pre-compile identifies who the caller is by verifying that msg.sender mapps to the token ID
// For example, for rune tokens, we need to check the CREATE2 formula, to come up with the same address
// Or for the case of BTC, we already know what is the BTC token address
// If a token is requesting withdrawal that is not supported we just revert the tx.
// Note: This should fail if there is not enough BTC to pay for the gas.
}

function extractSenderAndProxyFromTx(bytes32 txid) internal returns (address sender, address proxyWallet) {
// TODO: Call pre-compile
// This will decode a tx. First input will be the `sender` and
// the address inscripted in the script is the `proxyWallet`
}

/**
* TODO: Call pre-compile
* Parse the transaction, and extract calls.
* Verify that the msg.sender matches tokenId and version.
* Different processor may be invoked based on the token ID (e.g. BTC vs Rune)
*/
function processTx(uint tokenId, uint version, bytes32 txid) internal returns (
uint64 block,
uint64 timestamp,
TransferItem[] memory inputs,
TransferItem[] memory outputs,
bytes memory encodedCall // includes targetNetwork, beneficiary, targetContract, methodCall, fee
) {
}
}
Loading

0 comments on commit 990050a

Please sign in to comment.