Repository: VerusCoin/Verus-Ethereum-Contracts Branch: main Commit: d633dc403fe4 Files: 36 Total size: 276.8 KB Directory structure: gitextract_d6dc31im/ ├── .gitignore ├── contracts/ │ ├── Libraries/ │ │ ├── VerusConstants.sol │ │ ├── VerusConstantsProof.sol │ │ ├── VerusObjects.sol │ │ ├── VerusObjectsCommon.sol │ │ └── VerusObjectsNotarization.sol │ ├── MMR/ │ │ ├── Blake2b.sol │ │ ├── VerusBlake2b.sol │ │ ├── VerusMMR.sol │ │ └── VerusProof.sol │ ├── Main/ │ │ └── Delegator.sol │ ├── Migrations.sol │ ├── Storage/ │ │ └── StorageMaster.sol │ ├── VerusBridge/ │ │ ├── CreateExports.sol │ │ ├── ExportManager.sol │ │ ├── SubmitImports.sol │ │ ├── Token.sol │ │ ├── TokenManager.sol │ │ ├── UpgradeManager.sol │ │ ├── VerusCrossChainExport.sol │ │ ├── VerusSerializer.sol │ │ └── dsrinterface.sol │ └── VerusNotarizer/ │ ├── NotarizationSerializer.sol │ ├── NotaryTools.sol │ └── VerusNotarizer.sol ├── migrations/ │ ├── 1_initial_migration.js │ ├── 2_deploy_contracts.js │ └── setup.js ├── readme.md ├── run.bat ├── runtests.sh ├── runtruffle.bat └── test/ ├── deployed.js ├── helper.js ├── reservetransfer.ts └── submitnotarization.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ package-lock.json node_modules .vscode dist build/ *.json *.log truffle-config.js .env ================================================ FILE: contracts/Libraries/VerusConstants.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; // Developers notes for datatypes used: // uint176 types are used to store CTransferDestiantions as we are limited to a type (1byte) + vector length (1byte) + 20 bytes address. // These are used to allow the contract to process up to 50 transactions per CCE. When a currency import enters the contract through a transaction // The destination is a bytes array. library VerusConstants { uint256 constant public transactionFee = 3000000000000000; //0.003 ETH 18 decimals uint256 constant public upgradeFee = 1000000000000000; //0.001 ETH 18 decimals WEI TODO: increase for MAINNET uint64 constant public verusTransactionFee = 2000000; //0.02 VRSC 8 decimals uint64 constant public verusvETHTransactionFee = 300000; //0.003 vETH 8 decimals uint64 constant public verusvETHReturnFee = 1000000; //0.01 vETH 8 decimals uint64 constant public verusBridgeLaunchFeeShare = 500000000000; uint256 constant NOTARY_CLAIM_TX_GAS_COST = 310000; // gas required to run the notary fee claim function. uint256 constant GAS_BASE_COST_FOR_NOTARYS = 1100000; // 2 x submit imports 450k x 2 + base cost of submitimports.25k + 120K uint256 constant GAS_BASE_COST_FOR_REFUND_PAYOUTS = 20000; uint32 constant VALID = 1; uint32 constant CONVERT = 2; uint32 constant CROSS_SYSTEM = 0x40; uint32 constant BURN_CHANGE_PRICE = 0x80; uint32 constant IMPORT_TO_SOURCE = 0x200; uint32 constant RESERVE_TO_RESERVE = 0x400; uint32 constant CURRENCY_EXPORT = 0x2000; uint8 constant NFT_POSITION = 0; bytes32 constant VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL = 0x00000000000000000000000039aDf7BA6E5c91eeef476Bb4aC9417549ba0d51a; bytes32 constant VDXF_SYSTEM_DAI_HOLDINGS = 0x000000000000000000000000334711b41Cf095C9D44d1a209f34bf3559eA7640; address constant VDXF_ETH_DAI_VRSC_LAST_RESERVES = address(0x1b83EBE56D691b909cFb0dFc291E5A0EDAAfc64C); bytes32 constant VDXFID_DAI_DSR_SUPPLY = 0x00000000000000000000000084206E821f7bB4c6F390299c1367600F608c28C8; bytes32 constant SUBMIT_IMPORTS_LAST_TXID = 0x00000000000000000000000037256eef64a0bf17344bcb0cbfcde4bea6746347; bytes32 constant VDXFID_DAI_BURNBACK_TIME_THRESHOLD = 0x0000000000000000000000007d6505549c434ef651d799ede5f0d3f698464fcf; uint176 constant VDXFID_VETH_BURN_ADDRESS = 0x0214B26820ee0C9b1276Aac834Cf457026a575dfCe84; uint256 constant DAI_BURNBACK_THRESHOLD = 1000000000000000000000; //1000 DAI 18 decimals uint256 constant DAI_BURNBACK_TRANSACTION_GAS_AMOUNT = 594722; uint256 constant DAI_BURNBACK_MAX_FEE_THRESHOLD = 40000000000; //400 DAI in verus 8 decimals uint256 constant SECONDS_IN_DAY = 86400; uint256 constant REFUND_FEE_REIMBURSE_GAS_AMOUNT = 1000000; //1,000,000 GAS uint256 constant CLAIM_NOTARY_FEE_THRESHOLD = 0.75 ether; uint8 constant MINIMUM_TRANSACTIONS_FOR_REFUNDS = 8; uint8 constant MINIMUM_TRANSACTIONS_FOR_REFUNDS_HALF = 4; uint32 constant INVALID_FLAGS = 0xffffffff - (VALID + CONVERT + RESERVE_TO_RESERVE + IMPORT_TO_SOURCE); uint8 constant DEST_PK = 1; uint8 constant DEST_PKH = 2; uint8 constant DEST_ID = 4; uint8 constant DEST_REGISTERCURRENCY = 6; uint8 constant DEST_ETH = 9; uint8 constant DEST_ETHNFT = 10; uint8 constant FLAG_DEST_AUX = 64; uint8 constant FLAG_DEST_GATEWAY = 128; uint8 constant CURRENT_VERSION = 1; // deploy & launch Token flags These must match the constants in deploycontracts.js uint32 constant MAPPING_INVALID = 0; uint32 constant MAPPING_ETHEREUM_OWNED = 1; uint32 constant MAPPING_VERUS_OWNED = 2; uint32 constant MAPPING_PARTOF_BRIDGEVETH = 4; uint32 constant MAPPING_ISBRIDGE_CURRENCY = 8; uint32 constant MAPPING_ERC1155_NFT_DEFINITION = 16; uint32 constant MAPPING_ERC20_DEFINITION = 32; uint32 constant MAPPING_ERC1155_ERC_DEFINITION = 64; uint32 constant MAPPING_ERC721_NFT_DEFINITION = 128; // send flags uint32 constant TOKEN_ERC_SEND = 16; uint32 constant TOKEN_ETH_SEND = 64; uint32 constant AUX_DEST_PREFIX = 0x01160214; uint32 constant TICKER_LENGTH_MAX = 4; uint8 constant DESTINATION_PLUS_GATEWAY = 68; //notary flags uint8 constant NOTARY_VALID = 1; uint8 constant NOTARY_REVOKED = 2; //notarizationflags uint32 constant FLAG_CONTRACT_UPGRADE = 0x200; //cCurrencydefintion constants uint32 constant OPTION_NFT_TOKEN = 0x800; uint constant SATS_TO_WEI_STD = 10000000000; uint8 constant NUMBER_OF_CONTRACTS = 11; uint64 constant MIN_VRSC_FEE = 4000000; //0.04 VRSC 8 decimals uint64 constant MAX_VERUS_TRANSFER = 1000000000000000000; //10,000,000,000.00000000 uint constant VERUS_IMPORT_FEE = 2000000; //This is 0.02 VRSC 8 decimals uint constant VERUS_IMPORT_FEE_X2 = 4000000; //This is 2 x the fee 0.02 VRSC 8 decimals enum ContractType { TokenManager, VerusSerializer, VerusProof, VerusCrossChainExport, VerusNotarizer, CreateExport, VerusNotaryTools, ExportManager, SubmitImports, NotarizationSerializer, UpgradeManager } uint8 constant UINT160_SIZE = 20; uint8 constant UINT64_SIZE = 8; uint8 constant UINT160_BITS_SIZE = 160; uint8 constant UINT176_BITS_SIZE = 176; uint8 constant NOTARIZER_INDEX_AND_FLAGS_OFFSET = 184; uint8 constant NOTARIZATION_VOUT_NUM_INDEX = 192; uint8 constant NOTARIZATION_VALID_BIT_SHIFT = 7; //Global Generic Variable types uint8 constant GLOBAL_TYPE_NOTARY_VALID_HIGH_BIT = 0x80; uint8 constant GLOBAL_TYPE_NOTARY_MASK = 0x7f; uint8 constant VOTE_LENGTH = 50; uint8 constant DEFAULT_INDEX_VALUE = 200; uint256 constant MAX_UINT256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; } //TODO: extra constants to add 176, 92 etc.. //NOTE: Check constants with Mike ================================================ FILE: contracts/Libraries/VerusConstantsProof.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "./VerusObjectsCommon.sol"; library VerusObjectsProof { struct CMerkleBranch { uint8 CMerkleBranchBase; uint32 nIndex; uint32 nSize; uint8 extraHashes; bytes32[] branch; } struct CTXProof { uint8 branchType; CMerkleBranch proofSequence; } struct CComponents { uint8 elType; uint8 elIdx; bytes elVchObj; CTXProof[] elProof; } struct CPtransactionproof { uint8 version; uint8 typeC; CTXProof[] txproof; CComponents[] components; } } ================================================ FILE: contracts/Libraries/VerusObjects.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "./VerusObjectsCommon.sol"; library VerusObjects { struct blockCreated { uint index; bool created; } struct infoDetails { uint version; string VRSCversion; uint blocks; uint tiptime; string name; bool testnet; } struct currencyDetail { uint version; string name; address currencyid; address parent; address systemid; uint8 notarizationprotocol; uint8 proofprotocol; VerusObjectsCommon.CTransferDestination nativecurrencyid; address launchsystemid; uint startblock; uint endblock; uint64 initialsupply; uint64 prelaunchcarveout; address gatewayid; address[] notaries; uint minnotariesconfirm; } struct CCurrencyValueMap { address currency; uint64 amount; } struct CReserveTransfer { uint32 version; CCurrencyValueMap currencyvalue; uint32 flags; address feecurrencyid; uint64 fees; VerusObjectsCommon.CTransferDestination destination; address destcurrencyid; address destsystemid; address secondreserveid; } // CReserve Transfer Set is a simplified version of a crosschain export returning only the required info struct CReserveTransferSet { bytes32 exportHash; bytes32 prevExportHash; uint64 endHeight; CReserveTransfer[] transfers; } struct CReserveTransferSetCalled { bytes32 exportHash; bytes32 prevExportHash; uint64 startHeight; uint64 endHeight; CReserveTransfer[] transfers; } struct PackedSend { uint256 currencyAndAmount; //tokenID uint256 destinationAndFlags; //ETH address concatenated with flags } struct PackedCurrencyLaunch { uint256 tokenID; address ERCContract; //erc address address iaddress; address parent; uint8 flags; string name; } struct ETHPayments { address destination; // the start index of next read. when idx=b.length, we're done uint256 amount; // hold serialized data readonly } struct CReserveTransferImport { CPtransactionproof partialtransactionproof; bytes serializedTransfers; } struct CCrossChainExport { uint16 version; uint16 flags; address sourcesystemid; bytes32 hashtransfers; uint64 sourceheightstart; uint64 sourceheightend; address destinationsystemid; address destinationcurrencyid; uint32 numinputs; CCurrencyValueMap[] totalamounts; CCurrencyValueMap[] totalfees; CCurrencyValueMap[] totalburned; VerusObjectsCommon.CTransferDestination rewardaddress; //reward address int32 firstinput; } struct CMerkleBranch { uint8 CMerkleBranchBase; uint32 nIndex; uint32 nSize; uint8 extraHashes; bytes32[] branch; } struct CTXProof { uint8 branchType; CMerkleBranch proofSequence; } struct CComponents { uint8 elType; uint8 elIdx; bytes elVchObj; CTXProof[] elProof; } struct CPtransactionproof { uint8 version; uint8 typeC; CTXProof[] txproof; CComponents[] components; } struct mappedToken { address erc20ContractAddress; uint8 flags; uint tokenIndex; //Used for ERC20, ERC721 and ERC1155, total amount of tokens transfered to the bridge string name; uint256 tokenID; } struct setupToken { address iaddress; address erc20ContractAddress; address launchSystemID; uint8 flags; string name; string ticker; uint256 tokenID; } struct upgradeInfo { uint8 _vs; bytes32 _rs; bytes32 _ss; address[] contracts; uint8 upgradeType; bytes32 salt; address notarizerID; uint32 startHeight; } struct revokeInfo { uint8 _vs; bytes32 _rs; bytes32 _ss; address notaryID; bytes32 salt; } struct pendingUpgradetype { address notaryID; uint8 upgradeType; } struct notarizer { address main; address recovery; uint8 state; } struct lastImportInfo { bytes32 hashOfTransfers; bytes32 exporttxid; uint32 exporttxoutnum; uint32 height; } struct voteState { address txid; address[] pendingContracts; uint32 agree; uint32 count; uint32 startHeight; uint8 nullified; } struct revokeRecoverInfo{ uint8 _vs; bytes32 _rs; bytes32 _ss; bytes32 salt; address notarizerID; } } ================================================ FILE: contracts/Libraries/VerusObjectsCommon.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; library VerusObjectsCommon { struct CTransferDestination { uint8 destinationtype; bytes destinationaddress; } struct UintReader { uint32 offset; uint64 value; } } ================================================ FILE: contracts/Libraries/VerusObjectsNotarization.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "./VerusObjectsCommon.sol"; library VerusObjectsNotarization { struct CProofRoot{ int16 version; // to enable future data types with various functions int16 cprtype; // type of proof root address systemid; // system that can have things proven on it with this root uint32 rootheight; // height (or sequence) of the notarization we certify bytes32 stateroot; // latest MMR root of the notarization height bytes32 blockhash; // combination of block hash, block MMR root, and compact power (or external proxy) for the notarization height bytes32 compactpower; int64 gasprice; } struct CurrencyStates { address currencyid; CCoinbaseCurrencyState currencystate; } struct ProofRoots { address currencyid; CProofRoot proofroot; } struct CCoinbaseCurrencyState { uint16 version; uint16 flags; address currencyid; uint160[] currencies; int32[] weights; int64[] reserves; int64 initialsupply; int64 emitted; int64 supply; int64 primarycurrencyout; int64 preconvertedout; int64 primarycurrencyfees; int64 primarycurrencyconversionfees; int64[] reservein; // reserve currency converted to native int64[] primarycurrencyin; int64[] reserveout; // output can have both normal and reserve output value, if non-0, this is spent by the required output transactions int64[] conversionprice; // calculated price in reserve for all conversions * 100000000 int64[] viaconversionprice; // the via conversion stage prices int64[] fees; // fee values in native (or reserve if specified) coins for reserve transaction fees for the block int32[] priorweights; int64[] conversionfees; // total of only conversion fees, which will accrue to the conversion transaction } struct CUTXORef { bytes32 hash; uint32 n; } struct NotarizationForks { bytes32 hashOfNotarization; bytes32 txid; bytes32 proposerPacked; } struct CNodeData { string networkaddress; address nodeidentity; } struct CPBaaSNotarization { uint32 version; uint32 flags; VerusObjectsCommon.CTransferDestination proposer; address currencyid; CCoinbaseCurrencyState currencystate; uint32 notarizationheight; CUTXORef prevnotarization; bytes32 hashprevnotarization; uint32 prevheight; CurrencyStates[] currencystates; CProofRoot[] proofroots; CNodeData[] nodes; CUTXORef txid; } } ================================================ FILE: contracts/MMR/Blake2b.sol ================================================ /* * Blake2b library in Solidity using EIP-152 * * Copyright (C) 2019 Alex Beregszaszi * * License: Apache 2.0 */ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; pragma abicoder v2; library Blake2b { struct Instance { // This is a bit misleadingly called state as it not only includes the Blake2 state, // but every field needed for the "blake2 f function precompile". // // This is a tightly packed buffer of: // - rounds: 32-bit BE // - h: 8 x 64-bit LE // - m: 16 x 64-bit LE // - t: 2 x 64-bit LE // - f: 8-bit bytes state; // Expected output hash length. (Used in `finalize`.) uint out_len; // Data passed to "function F". // NOTE: this is limited to 24 bits. uint input_counter; } // Initialise the state with a given `key` and required `out_len` hash length. function init(bytes memory key, uint out_len) internal view returns (Instance memory instance) { // Safety check that the precompile exists. // TODO: remove this? // assembly { // if eq(extcodehash(0x09), 0) { revert(0, 0) } //} reset(instance, key, out_len); } // Initialise the state with a given `key` and required `out_len` hash length. function reset(Instance memory instance, bytes memory key, uint out_len) internal view { instance.out_len = out_len; instance.input_counter = 0; // This is entire state transmitted to the precompile. // It is byteswapped for the encoding requirements, additionally // the IV has the initial parameter block 0 XOR constant applied, but // not the key and output length. instance.state = hex"0000000c08c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b3DD8338ED89DE6791854126751AC933300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; bytes memory state = instance.state; // Update parameter block 0 with key length and output length. uint key_len = key.length; assembly { let ptr := add(state, 36) let tmp := mload(ptr) let p0 := or(shl(240, key_len), shl(248, out_len)) tmp := xor(tmp, p0) mstore(ptr, tmp) } // TODO: support salt and personalization if (key_len > 0) { require(key_len == 64); // FIXME: the key must be zero padded require(key.length == 128); update(instance, key, key_len); } } // This calls the blake2 precompile ("function F of the spec"). // It expects the state was updated with the next block. Upon returning the state will be updated, // but the supplied block data will not be cleared. function call_function_f(Instance memory instance) private view { bytes memory state = instance.state; assembly { let state_ptr := add(state, 32) if iszero(staticcall(not(0), 0x09, state_ptr, 0xd5, add(state_ptr, 4), 0x40)) { revert(0, 0) } } } // This function will split blocks correctly and repeatedly call the precompile. // NOTE: this is dumb right now and expects `data` to be 128 bytes long and padded with zeroes, // hence the real length is indicated with `data_len` function update_loop(Instance memory instance, bytes memory data, uint data_len, bool last_block) private view { bytes memory state = instance.state; uint input_counter = instance.input_counter; // This is the memory location where the "data block" starts for the precompile. uint state_ptr; assembly { // The `rounds` field is 4 bytes long and the `h` field is 64-bytes long. // Also adjust for the size of the bytes type. state_ptr := add(state, 100) } // This is the memory location where the input data resides. uint data_ptr; assembly { data_ptr := add(data, 32) } uint len = data.length; while (len > 0) { if (len >= 128) { assembly { mstore(state_ptr, mload(data_ptr)) data_ptr := add(data_ptr, 32) mstore(add(state_ptr, 32), mload(data_ptr)) data_ptr := add(data_ptr, 32) mstore(add(state_ptr, 64), mload(data_ptr)) data_ptr := add(data_ptr, 32) mstore(add(state_ptr, 96), mload(data_ptr)) data_ptr := add(data_ptr, 32) } len -= 128; // FIXME: remove this once implemented proper padding if (data_len < 128) { input_counter += data_len; } else { data_len -= 128; input_counter += 128; } } else { // FIXME: implement support for smaller than 128 byte blocks revert(); } // Set length field (little-endian) for maximum of 24-bits. assembly { mstore8(add(state, 228), and(input_counter, 0xff)) mstore8(add(state, 229), and(shr(8, input_counter), 0xff)) mstore8(add(state, 230), and(shr(16, input_counter), 0xff)) } // Set the last block indicator. // Only if we've processed all input. if (len == 0) { assembly { // Writing byte 212 here. mstore8(add(state, 244), last_block) } } // Call the precompile call_function_f(instance); } instance.input_counter = input_counter; } // Update the state with a non-final block. // NOTE: the input must be complete blocks. function update(Instance memory instance, bytes memory data, uint data_len) internal view { require((data.length % 128) == 0); update_loop(instance, data, data_len, false); } // Update the state with a final block and return the hash. function finalize(Instance memory instance, bytes memory data) internal view returns (bytes32 output) { // FIXME: support incomplete blocks (zero pad them) uint input_length = data.length; if (input_length == 0 || (input_length % 128) != 0) { data = concat(data, new bytes(128 - (input_length % 128))); } require((data.length % 128) == 0); update_loop(instance, data, input_length, true); // FIXME: support other lengths // require(instance.out_len == 64); bytes memory state = instance.state; assembly { output := mload(add(state, 36)) } } function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } } ================================================ FILE: contracts/MMR/VerusBlake2b.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; pragma abicoder v2; import "./Blake2b.sol"; library VerusBlake2b { using Blake2b for Blake2b.Instance; function createHash(bytes memory input) public view returns (bytes32) { Blake2b.Instance memory instance = Blake2b.init(hex"", 32); return instance.finalize(input); } } ================================================ FILE: contracts/MMR/VerusMMR.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; library VerusMMR { function GetMMRProofIndex(uint64 pos, uint64 mmvSize, uint8 extrahashes) public pure returns (uint64) { uint64 retIndex = 0; uint64 bitPos = 0; // find a path from the indicated position to the root in the current view if (pos > 0 && pos < mmvSize) { // calculate array size first so we can use memory instead of storage // use x and j for counters uint j = 0; uint64 x = mmvSize; // count most significant bit position while ((x >>= 1) > 0) { ++j; } // Allocate array of defined size uint64[] memory Sizes = new uint64[](j + 1); x = 0; Sizes[x++] = mmvSize; mmvSize >>= 1; while (mmvSize != 0) { Sizes[x++] = mmvSize; mmvSize >>= 1; } // next work out PeakIndexes size for array j = 0; for (int32 ht = int32(uint32(Sizes.length - 1)); ht != -1; ht--) { // if we're at the top or the layer above us is smaller than 1/2 the size of this layer, rounded up, we are a peak if (ht == int32(uint32(Sizes.length) - 1) || (Sizes[uint32(ht)] & 1) == 1) { j++; } } uint32[] memory PeakIndexes = new uint32[](j); x = 0; //reset x for (int32 ht = int32(uint32(Sizes.length - 1)); ht != -1; ht--) { // if we're at the top or the layer above us is smaller than 1/2 the size of this layer, rounded up, we are a peak if (ht == int32(uint32(Sizes.length) - 1) || (Sizes[uint32(ht)] & 1) == 1) { PeakIndexes[x++] = uint32(ht); } } // figure out the peak merkle uint64 layerNum = 0; uint64 layerSize = uint64(PeakIndexes.length); // with an odd number of elements below, the edge passes through //workout array size for MerkleSizes j = 0; x = 0; for (uint64 passThrough = (layerSize & 1); layerNum == 0 || layerSize > 1;) { layerSize = (layerSize >> 1) + passThrough; if (layerSize != 0) { j++; } passThrough = (layerSize & 1); layerNum++; } uint64[] memory MerkleSizes = new uint64[](j); //reset variables layerNum = 0; layerSize = uint64(PeakIndexes.length); for (uint64 passThrough = (layerSize & 1); layerNum == 0 || layerSize > 1;) { layerSize = (layerSize >> 1) + passThrough; if (layerSize != 0) { MerkleSizes[x++] = layerSize; } passThrough = (layerSize & 1); layerNum++; } // add extra hashes for a node on the right for (uint8 i = 0; i < extrahashes; i++) { // move to the next position bitPos++; } uint64 p = pos; for (uint l = 0; l < Sizes.length; l++) { if (p & 1 == 1) { retIndex |= (uint64(1) << bitPos++); p >>= 1; for (uint8 i = 0; i < extrahashes; i++) { bitPos++; } } else { // make sure there is one after us to hash with or we are a peak and should be hashed with the rest of the peaks if (Sizes[l] > (p + 1)) { bitPos++; p >>= 1; for (uint8 i = 0; i < extrahashes; i++) { bitPos++; } } else { for (p = 0; p < PeakIndexes.length; p++) { if (PeakIndexes[p] == l) { break; } } // p is the position in the merkle tree of peaks assert(p < PeakIndexes.length); // move up to the top, which is always a peak of size 1 int64 layerNumA = -1; uint64 layerSizeB; for (layerSizeB = uint64(PeakIndexes.length); layerNumA == -1 || layerSizeB > 1; layerSizeB = MerkleSizes[uint64(++layerNumA)]) { // printf("GetProofBits - Bits.size: %lu\n", Bits.size()); if (p < (layerSizeB - 1) || (p & 1) == 1) { if (p & 1 == 1) { // hash with the one before us retIndex |= (uint64(1)) << bitPos; bitPos++; for (uint8 i = 0; i < extrahashes; i++) { bitPos++; } } else { // hash with the one in front of us bitPos++; for (uint8 i = 0; i < extrahashes; i++) { bitPos++; } } } p >>= 1; } // finished break; } } } } return retIndex; } } ================================================ FILE: contracts/MMR/VerusProof.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.4.22; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "./VerusBlake2b.sol"; import {VerusSerializer} from "../VerusBridge/VerusSerializer.sol"; import "../Libraries/VerusObjectsCommon.sol"; import "../VerusNotarizer/VerusNotarizer.sol"; import "./VerusMMR.sol"; import "../Storage/StorageMaster.sol"; contract VerusProof is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; uint constant FORKS_DATA_OFFSET_FOR_HEIGHT = 224; uint constant FORKS_PROPOSER_SLOT = 2; constructor(address vETH, address Bridge, address Verus){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; } using VerusBlake2b for bytes; // these constants should be able to reference each other, as many are relative, but Solidity does not // allow referencing them and still considering the result a constant. For any changes to these constants, // which are designed to ensure smart transaction provability, care must be take to ensure that OUTPUT_SCRIPT_OFFSET // is present and substituted for all equivalent values (currently (32 + 8)) uint8 constant CCE_EVAL_EXPORT = 0xc; uint32 constant CCE_COPTP_HEADERSIZE = 24 + 1; uint32 constant CCE_COPTP_EVALOFFSET = 2; uint32 constant CCE_SOURCE_SYSTEM_OFFSET = 24; uint32 constant CCE_HASH_TRANSFERS_DELTA = 32; uint32 constant CCE_DEST_SYSTEM_DELTA = 20; uint32 constant CCE_DEST_CURRENCY_DELTA = 20; uint32 constant OUTPUT_SCRIPT_OFFSET = (8 + 1); // start of prevector serialization for output script uint32 constant SCRIPT_OP_CHECKCRYPTOCONDITION = 0xcc; uint32 constant SCRIPT_OP_PUSHDATA1 = 0x4c; uint32 constant SCRIPT_OP_PUSHDATA2 = 0x4d; uint32 constant TYPE_TX_OUTPUT = 4; uint32 constant SIZEOF_UINT64 = 8; uint8 constant FLAG_FRACTIONAL = 1; uint8 constant FLAG_REFUNDING = 4; uint8 constant FLAG_LAUNCHCONFIRMED = 0x10; uint8 constant FLAG_LAUNCHCOMPLETEMARKER = 0x20; uint8 constant AUX_DEST_ETH_VEC_LENGTH = 22; uint8 constant TX_HEADER = 1; uint8 constant NUM_TX_PROOFS = 3; bytes16 constant _SYMBOLS = "0123456789abcdef"; function checkProof(bytes32 hashToProve, VerusObjects.CTXProof[] memory _branches) public view returns(bytes32){ //loop through the branches from bottom to top bytes32 hashInProgress = hashToProve; require(_branches.length > 0); for(uint i = 0; i < _branches.length; i++){ hashInProgress = checkBranch(hashInProgress,_branches[i].proofSequence); } return hashInProgress; } function checkBranch(bytes32 _hashToCheck,VerusObjects.CMerkleBranch memory _branch) public view returns(bytes32){ require(_branch.branch.length > 0,"Branch must be longer than 0"); uint branchLength = _branch.branch.length; bytes32 hashInProgress = _hashToCheck; bytes memory joined; //hashInProgress = blake2b.bytesToBytes32(abi.encodePacked(_hashToCheck)); uint hashIndex = VerusMMR.GetMMRProofIndex(_branch.nIndex, _branch.nSize, _branch.extraHashes); for(uint i = 0;i < branchLength; i++){ if(hashIndex & 1 > 0){ require(_branch.branch[i] != _hashToCheck,"Value can be equal to node but never on the right"); //join the two arrays and pass to blake2b joined = abi.encodePacked(_branch.branch[i],hashInProgress); } else { joined = abi.encodePacked(hashInProgress,_branch.branch[i]); } hashInProgress = joined.createHash(); hashIndex >>= 1; } return hashInProgress; } function checkExportAndTransfers(VerusObjects.CReserveTransferImport memory _import, bytes32 hashedTransfers) public view returns (uint256, uint176) { // the first component of the import partial transaction proof is the transaction header, for each version of // transaction header, we have a specific offset for the hash of transfers. if we change this, we must // deprecate and deploy new contracts if(_import.partialtransactionproof.components[0].elType != TX_HEADER){ return (uint64(0), uint128(0)); } for (uint i = 1; i < _import.partialtransactionproof.components.length; i++) { if (_import.partialtransactionproof.components[i].elType != TYPE_TX_OUTPUT) continue; bytes memory firstObj = _import.partialtransactionproof.components[i].elVchObj; // we should have a first entry that is the txout with the export // ensure this is an export to VETH and pull the hash for reserve transfers // to ensure a valid export. // the eval code for the main COptCCParams must be EVAL_CROSSCHAIN_EXPORT, and the destination system // must match VETH uint32 nextOffset; uint8 var1; uint8 opCode2; uint32 nIndex; nIndex = _import.partialtransactionproof.components[i].elProof[0].proofSequence.nIndex; VerusObjectsCommon.UintReader memory readerLen; readerLen = VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]).readCompactSizeLE(firstObj, OUTPUT_SCRIPT_OFFSET); // get the length of the output script readerLen = VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]).readCompactSizeLE(firstObj, readerLen.offset); // then length of first master push // must be push less than 75 bytes, as that is an op code encoded similarly to a vector. // all we do here is ensure that is the case and skip master if (readerLen.value == 0 || readerLen.value > 0x4b) { return (uint64(0), uint128(0)); } nextOffset = uint32(readerLen.offset + readerLen.value); // add the length of the push of master to point to cc opcode //TODO: Check any other type would fail assembly { var1 := mload(add(firstObj, nextOffset)) // this should be OP_CHECKCRYPTOCONDITION nextOffset := add(nextOffset, 1) // and after that... opCode2 := mload(add(firstObj, nextOffset)) // should be OP_PUSHDATA1 or OP_PUSHDATA2 nextOffset := add(nextOffset, 1) // point to the length of the pushed data after CC instruction } if (var1 != SCRIPT_OP_CHECKCRYPTOCONDITION || (opCode2 != SCRIPT_OP_PUSHDATA1 && opCode2 != SCRIPT_OP_PUSHDATA2)) { return (uint64(0), uint128(0)); } if (opCode2 == SCRIPT_OP_PUSHDATA1) { assembly { nextOffset := add(nextOffset, 1) } } else { assembly { nextOffset := add(nextOffset, 2) } } // COptCCParams are serialized as pushes in a script, first the header, then the keys, then the data // skip right to the serialized export and then to the source system nextOffset += CCE_COPTP_EVALOFFSET; assembly { var1 := mload(add(firstObj, nextOffset)) } if (var1 != CCE_EVAL_EXPORT) { return (uint64(0), uint128(0)); } nextOffset += CCE_SOURCE_SYSTEM_OFFSET; assembly { opCode2 := mload(add(firstObj, nextOffset)) // should be OP_PUSHDATA1 or OP_PUSHDATA2 } nextOffset += CCE_COPTP_HEADERSIZE; if (opCode2 == SCRIPT_OP_PUSHDATA2) { nextOffset += 1; // one extra byte taken for varint } // validate source and destination values as well and set reward address return (checkCCEValues(firstObj, nextOffset, hashedTransfers, nIndex)); } return (uint256(0), uint176(0)); } function checkCCEValues(bytes memory firstObj, uint32 nextOffset, bytes32 hashedTransfers, uint32 nIndex) public view returns(uint256 tmpPacked, uint176 exporter) { bytes32 hashReserveTransfers; address systemSourceID; address destSystemID; uint32 tempRegister; uint8 tmpuint8; uint128 packedRegister; assembly { systemSourceID := mload(add(firstObj, nextOffset)) // source system ID, which should match expected source (VRSC/VRSCTEST) nextOffset := add(nextOffset, CCE_HASH_TRANSFERS_DELTA) hashReserveTransfers := mload(add(firstObj, nextOffset)) // get hash of reserve transfers from partial transaction proof nextOffset := add(nextOffset, CCE_DEST_SYSTEM_DELTA) destSystemID := mload(add(firstObj, nextOffset)) // destination system, which should be vETH nextOffset := add(nextOffset, CCE_DEST_CURRENCY_DELTA) // goto destcurrencyid nextOffset := add(nextOffset, 1) // goto exporter type tmpuint8 := mload(add(firstObj, nextOffset)) // read exporter type nextOffset := add(nextOffset, 1) // goto exporter destination length let lengthOfExporter := and(mload(add(firstObj, nextOffset)), 0xff) if gt(lengthOfExporter, 0) { nextOffset := add(nextOffset, lengthOfExporter) // goto exporter destination exporter := mload(add(firstObj, nextOffset)) // read exporter destination } } if (tmpuint8 & VerusConstants.FLAG_DEST_AUX == VerusConstants.FLAG_DEST_AUX) { nextOffset += 1; // goto auxdest parent vec length position (nextOffset, exporter) = readAuxDest(firstObj, nextOffset, exporter); //NOTE: If Auxdest present use address nextOffset -= 1; // NOTE: Next Varint call takes array pos not array pos +1 } assembly { nextOffset := add(nextOffset, 8) // move to read num inputs tempRegister := mload(add(firstObj, nextOffset)) // number of numInputs } (packedRegister, nextOffset) = readVarint(firstObj, nextOffset); // put startheight at [0] 32bit chunk tempRegister = serializeUint32(tempRegister); // reverse endian of no. transfers packedRegister |= (uint128(tempRegister) << 96) ; // put number of transfers at [3] 32-bit chunk (tempRegister, nextOffset) = readVarint(firstObj, nextOffset); packedRegister |= (uint128(tempRegister) << 32) ; // put endheight at [1] 32 bit chunk packedRegister |= (uint128(nIndex) << 64) ; // put nindex at [2] 32 bit chunk assembly { nextOffset := add(nextOffset, 1) // move to next byte for mapsize tmpuint8 := mload(add(firstObj, nextOffset)) } if (tmpuint8 == 1) { assembly { nextOffset := add(add(nextOffset, CCE_DEST_CURRENCY_DELTA), 8) // move 20 + 8 bytes for (address + 64bit) } } if (!(hashedTransfers == hashReserveTransfers && systemSourceID == VERUS && destSystemID == VETH)) { revert("CCE information does not checkout"); } tmpPacked = uint256(packedRegister); return (tmpPacked, exporter); } function readAuxDest (bytes memory firstObj, uint32 nextOffset, uint176 exporter) public view returns (uint32, uint176) { VerusObjectsCommon.UintReader memory readerLen; readerLen = VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]).readCompactSizeLE(firstObj, nextOffset); // get the length of the auxDest nextOffset = readerLen.offset; uint arraySize = readerLen.value; for (uint i = 0; i < arraySize; i++) { readerLen = VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]).readCompactSizeLE(firstObj, nextOffset); // get the length of the auxDest sub array if (readerLen.value == AUX_DEST_ETH_VEC_LENGTH) { assembly { exporter := mload(add(add(firstObj, nextOffset),AUX_DEST_ETH_VEC_LENGTH)) } } nextOffset = (readerLen.offset + uint32(readerLen.value)); } return (nextOffset, exporter); } // roll through each proveComponents function proveComponents(VerusObjects.CReserveTransferImport memory _import) public view returns(bytes32 txRoot){ bytes32 hashInProgress; bytes32 testHash; hashInProgress = _import.partialtransactionproof.components[0].elVchObj.createHash(); if (_import.partialtransactionproof.components[0].elType == TX_HEADER ) { txRoot = checkProof(hashInProgress, _import.partialtransactionproof.components[0].elProof); } for (uint i = 1; i < _import.partialtransactionproof.components.length; i++) { hashInProgress = _import.partialtransactionproof.components[i].elVchObj.createHash(); testHash = checkProof(hashInProgress, _import.partialtransactionproof.components[i].elProof); if (txRoot != testHash) { txRoot = bytes32(0); break; } } return txRoot; } function proveImports(bytes calldata dataIn ) external view returns(uint128, uint176){ (VerusObjects.CReserveTransferImport memory _import, bytes32 hashOfTransfers) = abi.decode(dataIn, (VerusObjects.CReserveTransferImport, bytes32)); bytes32 confirmedStateRoot; bytes32 retStateRoot; uint256 tmp; uint176 exporter; uint128 heightsAndTXNum; (tmp, exporter) = checkExportAndTransfers(_import, hashOfTransfers); heightsAndTXNum = uint128(tmp); bytes32 txRoot = proveComponents(_import); if(txRoot == bytes32(0)) { revert("Components do not validate"); } require(_import.partialtransactionproof.txproof.length == NUM_TX_PROOFS); retStateRoot = checkProof(txRoot, _import.partialtransactionproof.txproof); confirmedStateRoot = getLastConfirmedVRSCStateRoot(); if (retStateRoot == bytes32(0) || retStateRoot != confirmedStateRoot) { revert("Stateroot does not match"); } //truncate to only return heights as, contract will revert if issue with proofs. return (heightsAndTXNum, exporter); } function getLastConfirmedVRSCStateRoot() public view returns (bytes32) { bytes storage tempArray = bestForks[0]; if (tempArray.length == 0) { return bytes32(0); } bytes32 stateRoot; uint32 height; bytes32 slot; assembly { mstore(add(slot, 32),tempArray.slot) height := shr(FORKS_DATA_OFFSET_FOR_HEIGHT, sload(add(keccak256(add(slot, 32), 32), FORKS_PROPOSER_SLOT))) } tempArray = proofs[bytes32(uint256(height))]; assembly { mstore(add(slot, 32),tempArray.slot) stateRoot := sload(add(keccak256(add(slot, 32), 32), 0)) } return stateRoot; } function readVarint(bytes memory buf, uint idx) public pure returns (uint32 v, uint32 retidx) { uint8 b; assembly { ///assemmbly 2267 GAS let end := add(idx, 10) let i := idx retidx := add(idx, 1) for {} lt(i, end) {} { b := mload(add(buf, retidx)) i := add(i, 1) v := or(shl(7, v), and(b, 0x7f)) if iszero(eq(and(b, 0x80), 0x80)) { break } v := add(v, 1) retidx := add(retidx, 1) } } } function getTokenList(uint256 start, uint256 end) external returns(VerusObjects.setupToken[] memory ) { uint tokenListLength; tokenListLength = tokenList.length; VerusObjects.mappedToken memory recordedToken; uint i; uint endPoint; endPoint = tokenListLength - 1; if (start >= 0 && start < tokenListLength) { i = start; } if (end > i && end < tokenListLength) { endPoint = end; } VerusObjects.setupToken[] memory temp = new VerusObjects.setupToken[]((endPoint - i) + 1); uint j; for(; i <= endPoint; i++) { address iAddress; iAddress = tokenList[i]; recordedToken = verusToERC20mapping[iAddress]; temp[j].iaddress = iAddress; temp[j].flags = recordedToken.flags; if (iAddress == VETH) { temp[j].erc20ContractAddress = address(0); temp[j].name = recordedToken.name; temp[j].ticker = "ETH"; } else if(recordedToken.flags & VerusConstants.MAPPING_ERC20_DEFINITION == VerusConstants.MAPPING_ERC20_DEFINITION ) { temp[j].erc20ContractAddress = recordedToken.erc20ContractAddress; bytes memory tempName; tempName = bytes(recordedToken.name); if (tempName[1] == "." && tempName[2] == "." && tempName[3] == ".") { temp[j].name = string(abi.encodePacked("[", toHexString(uint160(recordedToken.erc20ContractAddress), 20), "]", slice(tempName, 5, tempName.length - 5))); } else { temp[j].name = recordedToken.name; } (bool success, bytes memory retval) = recordedToken.erc20ContractAddress.call(abi.encodeWithSignature("symbol()")); if (success && retval.length > 0x40) { temp[j].ticker = abi.decode(retval, (string)); } else if (retval.length == 0x20) { temp[j].ticker = string(slice(retval, 0, (retval[3] == 0 ? 3 : 4))); } else { temp[j].ticker = string(slice(bytes(temp[j].name), 3, 4)); } } else if(recordedToken.flags & VerusConstants.MAPPING_ERC721_NFT_DEFINITION == VerusConstants.MAPPING_ERC721_NFT_DEFINITION || recordedToken.flags & VerusConstants.MAPPING_ERC1155_NFT_DEFINITION == VerusConstants.MAPPING_ERC1155_NFT_DEFINITION || recordedToken.flags & VerusConstants.MAPPING_ERC1155_ERC_DEFINITION == VerusConstants.MAPPING_ERC1155_ERC_DEFINITION) { temp[j].erc20ContractAddress = recordedToken.erc20ContractAddress; temp[j].name = recordedToken.name; temp[j].tokenID = recordedToken.tokenID; } j++; } return temp; } function serializeUint32(uint32 number) public pure returns(uint32){ // swap bytes number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8); number = (number >> 16) | (number << 16); return number; } function toHexString(uint160 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { tempBytes := mload(0x40) let lengthmod := and(_length, 31) let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) mstore(0x40, and(add(mc, 31), not(31))) } default { tempBytes := mload(0x40) mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } } ================================================ FILE: contracts/Main/Delegator.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; pragma abicoder v2; import "../Storage/StorageMaster.sol"; import "../VerusBridge/Token.sol"; import "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; contract Delegator is VerusStorage, ERC1155Holder, ERC721Holder { address startOwner; constructor(address[] memory _notaries, address[] memory _notariesEthAddress, address[] memory _notariesColdStoreEthAddress, address[] memory _newContractAddress) { remainingLaunchFeeReserves = VerusConstants.verusBridgeLaunchFeeShare; for(uint i =0; i < _notaries.length; i++) { notaryAddressMapping[_notaries[i]] = VerusObjects.notarizer(_notariesEthAddress[i], _notariesColdStoreEthAddress[i], VerusConstants.NOTARY_VALID); notaries.push(_notaries[i]); } VerusNft t = new VerusNft(); //NOTE: VerusNFT contract is mapped by its unique contract address, as there is no i-address for it. verusToERC20mapping[address(t)] = VerusObjects.mappedToken(address(t), uint8(VerusConstants.MAPPING_VERUS_OWNED + VerusConstants.MAPPING_ERC721_NFT_DEFINITION), 0, "VerusNFT", uint256(0)); tokenList.push(address(t)); startOwner = msg.sender; for (uint i = 0; i < uint(VerusConstants.NUMBER_OF_CONTRACTS); i++) { contracts.push(_newContractAddress[i]); } } receive() external payable { } function sendTransfer(VerusObjects.CReserveTransfer calldata _transfer) external payable { bool success; bytes memory data = abi.encode(_transfer); address verusBridgeAddress = contracts[uint(VerusConstants.ContractType.CreateExport)]; (success, ) = verusBridgeAddress.delegatecall(abi.encodeWithSignature("sendTransfer(bytes)", data)); require(success); } function sendTransferDirect(bytes calldata data) external payable { bool success; address verusBridgeAddress = contracts[uint(VerusConstants.ContractType.CreateExport)]; (success, ) = verusBridgeAddress.delegatecall(abi.encodeWithSignature("sendTransferDirect(bytes)", data)); require(success); } function submitImports(VerusObjects.CReserveTransferImport calldata data) external { bool success; bytes memory returnedData; bytes memory packedData = abi.encode(data); address SubmitImportsAddress = contracts[uint(VerusConstants.ContractType.SubmitImports)]; (success, returnedData) = SubmitImportsAddress.delegatecall(abi.encodeWithSignature("_createImports(bytes)", packedData)); require(success); (uint64 fees, uint176 exporter) = abi.decode(returnedData, (uint64, uint176)); if (fees > 0 ) { (success,) = SubmitImportsAddress.delegatecall(abi.encodeWithSignature("setClaimableFees(uint64,uint176)", fees, exporter)); require(success); } } function getReadyExportsByRange(uint256 _startBlock, uint256 _endBlock) external returns(VerusObjects.CReserveTransferSetCalled[] memory returnedExports){ address logic = contracts[uint(VerusConstants.ContractType.SubmitImports)]; (bool success, bytes memory returnedData) = logic.delegatecall(abi.encodeWithSignature("getReadyExportsByRange(uint256,uint256)", _startBlock, _endBlock)); require(success); return abi.decode(returnedData, (VerusObjects.CReserveTransferSetCalled[])); } function setLatestData(bytes calldata serializedNotarization, bytes32 txid, uint32 n, bytes calldata data) external { address logic = contracts[uint(VerusConstants.ContractType.VerusNotarizer)]; (bool success, ) = logic.delegatecall(abi.encodeWithSignature("setLatestData(bytes,bytes32,uint32,bytes)", serializedNotarization, txid, n, data)); require(success); } function launchContractTokens(bytes calldata data) external { require(msg.sender == startOwner); require(tokenList.length == 1); address logic = contracts[uint(VerusConstants.ContractType.VerusNotaryTools)]; (bool success,) = logic.delegatecall(abi.encodeWithSignature("launchContractTokens(bytes)", data)); require(success); } function getTokenList(uint256 start, uint256 end) external returns(VerusObjects.setupToken[] memory ) { address logic = contracts[uint(VerusConstants.ContractType.VerusProof)]; (bool success, bytes memory returnedData) = logic.delegatecall(abi.encodeWithSignature("getTokenList(uint256,uint256)", start, end)); require(success); return abi.decode(returnedData, (VerusObjects.setupToken[])); } function checkImport(bytes32 _imports) external view returns(bool){ return processedTxids[_imports]; } function claimfees() external { address submitImportAddress = contracts[uint(VerusConstants.ContractType.SubmitImports)]; (bool success,) = submitImportAddress.delegatecall(abi.encodeWithSignature("claimfees()")); require(success); } function claimRefund(uint176 verusAddress, address currency) external payable returns (uint){ address submitImportAddress = contracts[uint(VerusConstants.ContractType.SubmitImports)]; (bool success, bytes memory returnedData) = submitImportAddress.delegatecall(abi.encodeWithSignature("claimRefund(uint176,address)", verusAddress, currency)); require(success); return abi.decode(returnedData, (uint)); } function sendfees(bytes32 publicKeyX, bytes32 publicKeyY) external { address submitImportAddress = contracts[uint(VerusConstants.ContractType.SubmitImports)]; (bool success,) = submitImportAddress.delegatecall(abi.encodeWithSignature("sendfees(bytes32,bytes32)", publicKeyX, publicKeyY)); require(success); } function getProof(uint256 proofHeightOptions) external payable returns (bytes memory) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotarizer)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("getProof(uint256)", proofHeightOptions)); require(success); return abi.decode(returnedData, (bytes)); } function getProofCosts(uint256 proofOption) external returns (uint256) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotarizer)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("getProofCosts(uint256)", proofOption)); require(success); return abi.decode(returnedData, (uint256)); } function upgradeContracts(bytes calldata data) external payable returns (uint8) { address upgradeManagerAddress = contracts[uint(VerusConstants.ContractType.UpgradeManager)]; (bool success, bytes memory returnedData) = upgradeManagerAddress.delegatecall(abi.encodeWithSignature("upgradeContracts(bytes)", data)); require(success); return abi.decode(returnedData, (uint8)); } function replacecontract(address newcontract, uint contractNo) external { require(msg.sender == startOwner); if(contractNo == 100) { startOwner = address(0); return; } contracts[contractNo] = newcontract; //NOTE: Upgraded contracts may need a initialize() function so they can setup things in a run once. (bool success,) = newcontract.delegatecall{gas: 3000000}(abi.encodeWithSignature("initialize()")); success; } function revokeWithMainAddress(bytes calldata data) external returns (bool) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotaryTools)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("revokeWithMainAddress(bytes)", data)); require(success); return abi.decode(returnedData, (bool)); } function revokeWithMultiSig(bytes calldata data) external returns (bool) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotaryTools)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("revokeWithMultiSig(bytes)", data)); require(success); return abi.decode(returnedData, (bool)); } function recoverWithRecoveryAddress(bytes calldata data) external returns (uint8) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotaryTools)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("recoverWithRecoveryAddress(bytes)", data)); require(success); return abi.decode(returnedData, (uint8)); } function recoverWithMultiSig(bytes calldata data) external returns (uint8) { address VerusNotaryToolsAddress = contracts[uint(VerusConstants.ContractType.VerusNotaryTools)]; (bool success, bytes memory returnedData) = VerusNotaryToolsAddress.delegatecall(abi.encodeWithSignature("recoverWithMultiSig(bytes)", data)); require(success); return abi.decode(returnedData, (uint8)); } function getVoteCount(address contractsHash) external returns (uint) { address upgradeManagerAddress = contracts[uint(VerusConstants.ContractType.UpgradeManager)]; (bool success, bytes memory returnedData) = upgradeManagerAddress.delegatecall(abi.encodeWithSignature("getVoteCount(address)", contractsHash)); require(success); return abi.decode(returnedData, (uint)); } function burnFees(bytes calldata data) external { address CreateExportsAddress = contracts[uint(VerusConstants.ContractType.CreateExport)]; (bool success,) = CreateExportsAddress.delegatecall(abi.encodeWithSignature("burnFees(bytes)", data)); require(success); } function setVerusData(bytes calldata data, string calldata vdxfid) external { address VerusCrossChainExportAddress = contracts[uint(VerusConstants.ContractType.VerusCrossChainExport)]; bool success; bytes memory returnedData; (success, returnedData) = VerusCrossChainExportAddress.delegatecall(abi.encodeWithSignature("checkVDFXId(string)", vdxfid)); require(success); uint returnedContractIndex; (returnedContractIndex) = abi.decode(returnedData, (uint)); string memory func = string(abi.encodePacked(vdxfid,"(bytes)")); (success,) = contracts[returnedContractIndex].delegatecall(abi.encodeWithSignature(func, data)); require(success); } } ================================================ FILE: contracts/Migrations.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; contract Migrations { address public owner; uint public last_completed_migration; constructor() { owner = msg.sender; } modifier restricted() { if (msg.sender == owner) _; } function setCompleted(uint completed) public restricted { last_completed_migration = completed; } function upgrade(address new_address) public restricted { Migrations upgraded = Migrations(new_address); upgraded.setCompleted(last_completed_migration); } } ================================================ FILE: contracts/Storage/StorageMaster.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; pragma abicoder v2; import "../VerusBridge/Token.sol"; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusConstants.sol"; import "../Libraries/VerusObjectsNotarization.sol"; contract VerusStorage { //verusbridgestorage mapping (uint => VerusObjects.CReserveTransferSet) public _readyExports; mapping (uint => uint) public exportHeights; mapping (bytes32 => bool) public processedTxids; mapping (address => VerusObjects.mappedToken) public verusToERC20mapping; mapping (bytes32 => VerusObjects.lastImportInfo) public lastImportInfo; address[] public tokenList; bytes32 public lastTxIdImport; //NOTE: not used and changed to a VDXFID uint64 public cceLastStartHeight; uint64 public cceLastEndHeight; //verusnotarizer storage bool public bridgeConverterActive; mapping (bytes32 => bytes) public storageGlobal; // Generic storage location NOTE: After security verified, add Oracle proofs. mapping (bytes32 => bytes) internal proofs; // Stored Verus stateroot/blockhash proofs indexed by height. mapping (bytes32 => uint256) public claimableFees; // CreserveTransfer destinations mapped to Fees they have accrued. mapping (bytes32 => mapping(address => uint256)) public refunds; // Failed transaction refunds uint64 remainingLaunchFeeReserves; // Starts at 5000 VRSC //upgrademanager address[] public contracts; // List of all known contracts Delegator trusts to use (contracts replacable on upgrade) mapping (address => uint) successfulVoteHashes; // Used hashes of contract upgrades address[100] public rollingUpgradeVotes; uint8 public rollingVoteIndex; mapping (bytes32 => bool) public saltsUsed; //salts used for upgrades and revoking. // verusnotarizer mapping (address => VerusObjects.notarizer ) public notaryAddressMapping; // Mapping iaddress of notaries to their spend/recover ETH addresses mapping (bytes32 => bool) knownNotarizationTxids; address[] public notaries; // Notaries for enumeration bytes[] public bestForks; // Forks array address public owner; // Testnet only owner to allow quick upgrades, TODO: Remove once Voting established. } ================================================ FILE: contracts/VerusBridge/CreateExports.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusConstants.sol"; import "./Token.sol"; import "./VerusCrossChainExport.sol"; import "./CreateExports.sol"; import "../Storage/StorageMaster.sol"; import "./ExportManager.sol"; import '@openzeppelin/contracts/token/ERC1155/IERC1155.sol'; import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; import "./SubmitImports.sol"; contract CreateExports is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; address immutable DAI; address immutable DAIERC20ADDRESS; enum Currency {VETH, DAI, VERUS, MKR} using SafeERC20 for Token; constructor(address vETH, address Bridge, address Verus, address Dai, address daiERC20Address){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; DAI = Dai; DAIERC20ADDRESS = daiERC20Address; } function initialize() external { // NOTE: the following code was deployed and is running. Be sure to include an initialization function in the constructor for any future changes to the contract that would require initialization logic, as the constructor will not be run on an upgrade. // Correct the DAI holdings to account for any DAI already in DSR at deployment time. // verusToERC20mapping[DAI].tokenIndex = (claimableFees[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS] / VerusConstants.SATS_TO_WEI_STD) - verusToERC20mapping[DAI].tokenIndex; } // ******* 2026-01-05 NOTE ******* // contract now launched so fees must always be paid by user. (contract size reduction) // function subtractPoolSize(uint64 _amount) private returns (bool) { // if((_amount + VerusConstants.MIN_VRSC_FEE) > remainingLaunchFeeReserves) return false; // remainingLaunchFeeReserves -= _amount; // return true; // } // ******************************* function sendTransfer(bytes calldata datain) payable external { VerusObjects.CReserveTransfer memory transfer = abi.decode(datain, (VerusObjects.CReserveTransfer)); sendTransferMain(transfer); } function sendTransferDirect(bytes calldata datain) payable external { address serializerAddress = contracts[uint(VerusConstants.ContractType.VerusSerializer)]; (bool success, bytes memory returnData) = serializerAddress.call(abi.encodeWithSignature("deserializeTransfer(bytes)",datain)); require(success, "deserializetransfer failed"); VerusObjects.CReserveTransfer memory transfer = abi.decode(returnData, (VerusObjects.CReserveTransfer)); sendTransferMain(transfer); } function sendTransferMain(VerusObjects.CReserveTransfer memory transfer) private { uint256 fees; VerusObjects.mappedToken memory iaddressMapping; uint32 ethNftFlag; address verusExportManagerAddress = contracts[uint(VerusConstants.ContractType.ExportManager)]; (bool success, bytes memory returnData) = verusExportManagerAddress.delegatecall(abi.encodeWithSelector(ExportManager.checkExport.selector, transfer)); require(success, "checkExport call failed"); fees = abi.decode(returnData, (uint256)); require(fees != 0, "CheckExport Failed Checks"); // ******* 2026-01-05 NOTE ******* // bridgeConverterActive == true, so fees must always be paid by user. // if(!bridgeConverterActive) { // require (subtractPoolSize(uint64(transfer.fees))); // } // ******************************* if (transfer.currencyvalue.currency != VETH) { iaddressMapping = verusToERC20mapping[transfer.currencyvalue.currency]; ethNftFlag = iaddressMapping.flags & (VerusConstants.MAPPING_ERC721_NFT_DEFINITION | VerusConstants.MAPPING_ERC1155_NFT_DEFINITION | VerusConstants.MAPPING_ERC1155_ERC_DEFINITION); } uint balance; if (ethNftFlag & (VerusConstants.MAPPING_ERC1155_NFT_DEFINITION | VerusConstants.MAPPING_ERC1155_ERC_DEFINITION) != 0) { IERC1155 nft = IERC1155(iaddressMapping.erc20ContractAddress); // TokenIndex is used for the amount of tokens held by the bridge (for erc1155's and ERC721's) if (ethNftFlag == VerusConstants.MAPPING_ERC1155_ERC_DEFINITION) { require((transfer.currencyvalue.amount + iaddressMapping.tokenIndex) < VerusConstants.MAX_VERUS_TRANSFER); } else { require(iaddressMapping.tokenIndex == 0); } verusToERC20mapping[transfer.currencyvalue.currency].tokenIndex += transfer.currencyvalue.amount; require (nft.isApprovedForAll(msg.sender, address(this)), "NFT not approved"); balance = nft.balanceOf(address(this), iaddressMapping.tokenID); nft.safeTransferFrom(msg.sender, address(this), iaddressMapping.tokenID, transfer.currencyvalue.amount, ""); require(nft.balanceOf(address(this), iaddressMapping.tokenID) == balance + transfer.currencyvalue.amount); } else if (ethNftFlag == VerusConstants.MAPPING_ERC721_NFT_DEFINITION) { VerusNft nft = VerusNft(iaddressMapping.erc20ContractAddress); require (nft.getApproved(iaddressMapping.tokenID) == address(this), "NFT not approved"); require(iaddressMapping.tokenIndex == 0); balance = nft.balanceOf(address(this)); nft.safeTransferFrom(msg.sender, address(this), iaddressMapping.tokenID, ""); require(nft.balanceOf(address(this)) == balance + 1); if (iaddressMapping.erc20ContractAddress == verusToERC20mapping[tokenList[VerusConstants.NFT_POSITION]].erc20ContractAddress) { nft.burn(iaddressMapping.tokenID); } else { //Only non-verus ERC721 NFTs need their accounting to be checked as they could be non standard. verusToERC20mapping[transfer.currencyvalue.currency].tokenIndex += transfer.currencyvalue.amount; } } else if (transfer.currencyvalue.currency != VETH) { Token token = Token(iaddressMapping.erc20ContractAddress); //Check user has allowed the verusBridgeStorage contract to spend on their behalf uint256 allowedTokens = token.allowance(msg.sender, address(this)); uint256 tokenAmount = convertFromVerusNumber(transfer.currencyvalue.amount, token.decimals()); //convert to wei from verus satoshis require( allowedTokens >= tokenAmount); if (iaddressMapping.flags & VerusConstants.MAPPING_ETHEREUM_OWNED == VerusConstants.MAPPING_ETHEREUM_OWNED) { // tokenIndex is used for the amount of tokens held by the bridge ERC20's require((transfer.currencyvalue.amount + iaddressMapping.tokenIndex) < VerusConstants.MAX_VERUS_TRANSFER); verusToERC20mapping[transfer.currencyvalue.currency].tokenIndex += transfer.currencyvalue.amount; } exportERC20Tokens(tokenAmount, token, iaddressMapping.flags & VerusConstants.MAPPING_VERUS_OWNED == VerusConstants.MAPPING_VERUS_OWNED); } _createExports(transfer, false); } function exportERC20Tokens(uint256 _tokenAmount, Token token, bool burn) public { uint256 balance; balance = token.balanceOf(address(this)); token.safeTransferFrom(msg.sender, address(this), _tokenAmount); require(token.balanceOf(address(this)) == balance + _tokenAmount, "ERC20 transfer failed"); // If the token is DAI, run join to have the DAI transferred to the DSR contract. if (address(token) == DAIERC20ADDRESS) { address crossChainExportAddress = contracts[uint(VerusConstants.ContractType.VerusCrossChainExport)]; (bool success,) = crossChainExportAddress.delegatecall(abi.encodeWithSelector(VerusCrossChainExport.join.selector, _tokenAmount)); require(success); } if (burn) { token.burn(_tokenAmount); } } function externalCreateExportCallPayable(bytes memory data) external payable { (VerusObjects.CReserveTransfer memory reserveTransfer, bool forceNewCCE) = abi.decode(data, (VerusObjects.CReserveTransfer, bool)); _createExports(reserveTransfer, forceNewCCE); } function externalCreateExportCall(bytes memory data) external { (VerusObjects.CReserveTransfer memory reserveTransfer, bool forceNewCCE) = abi.decode(data, (VerusObjects.CReserveTransfer, bool)); _createExports(reserveTransfer, forceNewCCE); } function _createExports(VerusObjects.CReserveTransfer memory reserveTransfer, bool forceNewCCE) private { // If transactions over 50 and inbetween notarization boundaries, increment CCE start and endheight // If notarization has happened increment CCE to next boundary when the tx comes in // If changing from pool closed to pool open create a boundary (As all sends will then go through the bridge) uint64 blockNumber = uint64(block.number); uint64 blockDelta = blockNumber - cceLastStartHeight; uint64 lastTransfersLength = uint64(_readyExports[cceLastStartHeight].transfers.length); bytes32 prevHash = _readyExports[cceLastStartHeight].exportHash; // if there are no transfers then there is no need to make a new CCE as this is the first one, and the endheight can become the block number if it is less than the current block no. // if the last notary received height is less than the endheight then keep building up the CCE (as long as 10 ETH blocks havent passed, and a new CCE isnt being forced and there is less than 50) if ((cceLastEndHeight == 0 || blockDelta < 10) && !forceNewCCE && lastTransfersLength < 50) { // set the end height of the CCE to the current block.number only if the current block we are on is greater than its value if (cceLastEndHeight < blockNumber) { cceLastEndHeight = blockNumber; } // if a new CCE is triggered for any reason, its startblock is always the previous endblock +1, // its start height may of spilled in to virtual future block numbers so if the current cce start height is less than the block we are on we can update the end // height to a new greater value. Otherwise if the startheight is still in the future then the endheight is also in the future at the same block. } else { cceLastStartHeight = cceLastEndHeight + 1; if (cceLastStartHeight < blockNumber) { cceLastEndHeight = blockNumber; } else { cceLastEndHeight = cceLastStartHeight; } } if (exportHeights[cceLastEndHeight] != cceLastStartHeight) { exportHeights[cceLastEndHeight] = cceLastStartHeight; } setReadyExportTransfers(cceLastStartHeight, cceLastEndHeight, reserveTransfer, 50); VerusObjects.CReserveTransferSet memory pendingTransfers = _readyExports[cceLastStartHeight]; address crossChainExportAddress = contracts[uint(VerusConstants.ContractType.VerusCrossChainExport)]; (bool success, bytes memory returnData) = crossChainExportAddress.call(abi.encodeWithSignature("generateCCE(bytes)", abi.encode(pendingTransfers.transfers, bridgeConverterActive, cceLastStartHeight, cceLastEndHeight, contracts[uint(VerusConstants.ContractType.VerusSerializer)]))); require(success, "generateCCEfailed"); bytes memory serializedCCE = abi.decode(returnData, (bytes)); if(pendingTransfers.transfers.length > 1) { prevHash = pendingTransfers.prevExportHash; } setReadyExportTxid(keccak256(abi.encodePacked(serializedCCE, prevHash)), prevHash, cceLastStartHeight); } function setReadyExportTxid(bytes32 txidhash, bytes32 prevTxidHash, uint _block) private { _readyExports[_block].exportHash = txidhash; if (_readyExports[_block].transfers.length == 1) { _readyExports[_block].prevExportHash = prevTxidHash; } } function setReadyExportTransfers(uint64 _startHeight, uint64 _endHeight, VerusObjects.CReserveTransfer memory reserveTransfer, uint blockTxLimit) private { _readyExports[_startHeight].endHeight = _endHeight; _readyExports[_startHeight].transfers.push(reserveTransfer); require(_readyExports[_startHeight].transfers.length <= blockTxLimit); } function burnFees(bytes calldata) external { require(bridgeConverterActive, "Bridge Converter not active"); uint256 interestAccrued; uint64 truncatedInterest; bool success; bytes memory retData; // NOTE: Accrued interest is truncated from 18 decimals to 8, to be compatible with verus SATS. address crossChainExportAddress = contracts[uint(VerusConstants.ContractType.VerusCrossChainExport)]; (success, retData) = crossChainExportAddress.delegatecall(abi.encodeWithSelector(VerusCrossChainExport.daiBalance.selector)); require(success); mapping (bytes32 => uint256) storage daiTotals = claimableFees; interestAccrued = abi.decode(retData, (uint256)) - daiTotals[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS]; uint256 bridgeReserveValues = daiTotals[bytes32(uint256(uint160(VerusConstants.VDXF_ETH_DAI_VRSC_LAST_RESERVES)))]; // Multiply the cost of the Transaction to send DAI to Verus in vETH (8 decimals) by the amount of reservers in DAI. uint daiCalculation = (tx.gasprice * VerusConstants.DAI_BURNBACK_TRANSACTION_GAS_AMOUNT)/ VerusConstants.SATS_TO_WEI_STD * (uint64(bridgeReserveValues >> (uint(Currency.DAI) << 6))); // Divide the previous value by the amount of reserves in vETH (8 decimals) to get the price of the ETH transaction in DAI. uint64 DAIReimburseAmount = uint64(daiCalculation / uint64(bridgeReserveValues >> (uint(Currency.VETH) << 6))); require (DAIReimburseAmount < VerusConstants.DAI_BURNBACK_MAX_FEE_THRESHOLD && daiTotals[VerusConstants.VDXFID_DAI_BURNBACK_TIME_THRESHOLD] + VerusConstants.SECONDS_IN_DAY < block.timestamp, "Fee too high or not enough time passed"); daiTotals[VerusConstants.VDXFID_DAI_BURNBACK_TIME_THRESHOLD] = block.timestamp; //truncate DAI to 8 DECIMALS of precision from 18 truncatedInterest = uint64(interestAccrued / VerusConstants.SATS_TO_WEI_STD); // Recalculate the interest accrued by subtracting the amount of DAI to be reimbursed. interestAccrued = (truncatedInterest * VerusConstants.SATS_TO_WEI_STD) - DAIReimburseAmount; // The interest accrued must be a significant amount to be worth sending back to Verus. require (interestAccrued > VerusConstants.DAI_BURNBACK_THRESHOLD); // Increase the supply of DAI by the amount of interest accrued - minus the payback fee. daiTotals[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS] += interestAccrued; uint64 fees; (success, retData) = contracts[uint(VerusConstants.ContractType.SubmitImports)] .delegatecall(abi.encodeWithSelector(SubmitImports.getImportFeeForReserveTransfer.selector, DAI)); require(success); fees = abi.decode(retData, (uint64)); require (truncatedInterest > fees, "Not enough DAI to pay fees"); (success, retData) = contracts[uint(VerusConstants.ContractType.SubmitImports)] .delegatecall(abi.encodeWithSelector(SubmitImports.sendBurnBackToVerus.selector, truncatedInterest, DAI, fees)); require(success); // When the bridge launches to make sure a fresh block with no pending vrsc transfers is used as not to mix destination currencies. (VerusObjects.CReserveTransfer memory LPtransfer,) = abi.decode(retData, (VerusObjects.CReserveTransfer, bool)); _createExports(LPtransfer, false); //transfer DAI to the msg.senders address. (success,) = crossChainExportAddress.delegatecall( abi.encodeWithSelector( VerusCrossChainExport.exit.selector, msg.sender, DAIReimburseAmount * VerusConstants.SATS_TO_WEI_STD)); require(success); // Add the amount sent to Verus to the DAI holdings. verusToERC20mapping[DAI].tokenIndex += truncatedInterest; } function convertFromVerusNumber(uint256 a,uint8 decimals) public pure returns (uint256) { uint8 power = 10; //default value for 18 uint256 c = a; if(decimals > 8 ) { power = decimals - 8;// number of decimals in verus c = a * (10 ** power); }else if(decimals < 8){ power = 8 - decimals;// number of decimals in verus c = a / (10 ** power); } return c; } } ================================================ FILE: contracts/VerusBridge/ExportManager.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusObjectsCommon.sol"; import "../Libraries/VerusConstants.sol"; import "../Storage/StorageMaster.sol"; contract ExportManager is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; bool runonce; constructor(address vETH, address Bridge, address Verus){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; } uint8 constant UINT160_SIZE = 20; uint8 constant FEE_OFFSET = 20 + 20 + 20 + 8; // 3 x 20bytes address + 64bit uint // 3 x 20bytes address + 64bit uint // Aux dest can only be one vector, of one vector which is a CTransferDestiantion of an R address. uint8 constant AUX_DEST_LENGTH = 24; function ERC20Registered(address _iaddress) private view returns (bool) { return verusToERC20mapping[_iaddress].flags > 0; } function checkExport(VerusObjects.CReserveTransfer memory transfer) external payable returns (uint256 fees){ uint256 requiredFees = VerusConstants.transactionFee; //0.003 eth in WEI (To vrsc) NOTE: convert to VRSC from ETH. int64 bounceBackFee; int64 transferFee; bytes memory serializedDest; address gatewayID; address gatewayCode; address destAddressID; require (checkTransferFlags(transfer), "Flag Check failed"); serializedDest = transfer.destination.destinationaddress; assembly { destAddressID := mload(add(serializedDest, UINT160_SIZE)) } if (verusToERC20mapping[transfer.currencyvalue.currency].flags & (VerusConstants.MAPPING_ERC721_NFT_DEFINITION | VerusConstants.MAPPING_ERC1155_NFT_DEFINITION | VerusConstants.MAPPING_ERC1155_ERC_DEFINITION) != 0) { require (transfer.flags == VerusConstants.VALID, "Invalid flags for NFT transfer"); require (transfer.currencyvalue.amount == 1 || verusToERC20mapping[transfer.currencyvalue.currency].flags & VerusConstants.MAPPING_ERC1155_ERC_DEFINITION == VerusConstants.MAPPING_ERC1155_ERC_DEFINITION, "Currency value must be 1 Satoshi"); require (serializedDest.length == VerusConstants.UINT160_SIZE, "destination address not 20 bytes"); } // Check destination address is not zero require (destAddressID != address(0), "Destination Address null"); require (transfer.currencyvalue.currency != transfer.secondreserveid, "Cannot convert like for like"); if (!bridgeConverterActive) { require (transfer.feecurrencyid == VERUS, "feecurrencyid != vrsc"); if (transfer.destination.destinationtype == (VerusConstants.DEST_ETH + VerusConstants.FLAG_DEST_GATEWAY + VerusConstants.FLAG_DEST_AUX) || transfer.flags != VerusConstants.VALID) return 0; require (transfer.destination.destinationaddress.length == VerusConstants.UINT160_SIZE, "destination address not 20 bytes"); } else { transferFee = int64(transfer.fees); require(transfer.feecurrencyid == VETH, "Fee Currency not vETH"); //TODO:Accept more fee currencies if (transfer.destination.destinationtype == (VerusConstants.FLAG_DEST_GATEWAY + VerusConstants.DEST_ETH + VerusConstants.FLAG_DEST_AUX)) { // destinationaddress is concatenated with the gateway back address (bridge.veth) + (gatewayCode) + 0.003 ETH in fees uint64LE // destinationaddress is also concatenated with aux dest assembly { gatewayID := mload(add(serializedDest, 40)) // second 20bytes in bytes array gatewayCode := mload(add(serializedDest, 60)) // third 20bytes in bytes array bounceBackFee := mload(add(serializedDest, FEE_OFFSET)) } require (transfer.destination.destinationaddress.length == (FEE_OFFSET + AUX_DEST_LENGTH), "destination address not 68 + 24 bytes"); uint32 auxDestPrefix; address auxDestAddress; assembly { auxDestPrefix := mload(add(add(serializedDest, FEE_OFFSET), 4)) // 4bytes AUX_DEST_PREFIX_CONSTANT auxDestAddress := mload(add(add(serializedDest, FEE_OFFSET), 24)) // destaddress } require (auxDestPrefix == VerusConstants.AUX_DEST_PREFIX, "auxDestPrefix Incorrect"); require (auxDestAddress != address(0), "auxDestAddress must not be empty"); require (gatewayID == VETH, "GatewayID not VETH"); require (gatewayCode == address(0), "GatewayCODE must be empty"); bounceBackFee = reverse(uint64(bounceBackFee)); //TODO: Change bounce back fee to be the calculated fee. require (bounceBackFee >= int64(VerusConstants.verusvETHReturnFee), "Return fee not >= 0.01ETH"); transferFee += bounceBackFee; requiredFees += convertFromVerusNumber(uint64(bounceBackFee),18); //bounceback fees required as well as send fees } else if (!(transfer.destination.destinationtype == VerusConstants.DEST_PKH || transfer.destination.destinationtype == VerusConstants.DEST_ID)) { return 0; } } // Check fees are included in the ETH value if sending ETH, or are equal to the fee value for tokens. uint amount; amount = transfer.currencyvalue.amount; if (bridgeConverterActive) { if (convertFromVerusNumber(uint64(transferFee), 18) < requiredFees) { revert ("ETH Fees to Low"); } else if (transfer.currencyvalue.currency == VETH && msg.value < convertFromVerusNumber(uint256(amount + uint64(transferFee)), 18)) { revert ("ETH sent < (amount + fees)"); } else if (transfer.currencyvalue.currency != VETH && msg.value < convertFromVerusNumber(uint64(transferFee), 18)) { revert ("ETH fee sent < fees for token"); } return uint64(transferFee); } else { if (transfer.fees != VerusConstants.verusTransactionFee) { revert ("Invalid VRSC fee"); } else if (transfer.currencyvalue.currency == VETH && (convertFromVerusNumber(amount, 18) + requiredFees) != msg.value) { revert ("ETH Fee to low"); } else if(transfer.currencyvalue.currency != VETH && requiredFees != msg.value) { revert ("ETH Fee to low (token)"); } claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] += VerusConstants.verusvETHTransactionFee; } return requiredFees; } function checkTransferFlags(VerusObjects.CReserveTransfer memory transfer) public view returns(bool) { require(transfer.destsystemid == address(0), "currencycheckfailed"); if (transfer.version != VerusConstants.CURRENT_VERSION || (transfer.flags & (VerusConstants.INVALID_FLAGS | VerusConstants.VALID) ) != VerusConstants.VALID) { revert ("Invalid Flag used"); } VerusObjects.mappedToken memory sendingCurrency = verusToERC20mapping[transfer.currencyvalue.currency]; VerusObjects.mappedToken memory destinationCurrency = verusToERC20mapping[transfer.destcurrencyid]; if (!(transfer.destination.destinationtype == (VerusConstants.DEST_ETH + VerusConstants.FLAG_DEST_GATEWAY + VerusConstants.FLAG_DEST_AUX) || transfer.destination.destinationtype == VerusConstants.DEST_ID || transfer.destination.destinationtype == VerusConstants.DEST_PKH) || sendingCurrency.flags == 0) { revert ("Invalid desttype"); } if (transfer.flags == VerusConstants.VALID && transfer.secondreserveid == address(0)) { require ((transfer.destcurrencyid == (bridgeConverterActive ? BRIDGE : VERUS) && (transfer.destination.destinationtype == VerusConstants.DEST_ID || transfer.destination.destinationtype == VerusConstants.DEST_PKH)), "Invalid desttype"); } else if (transfer.flags == (VerusConstants.VALID + VerusConstants.CONVERT + VerusConstants.RESERVE_TO_RESERVE)) { require(sendingCurrency.flags & VerusConstants.MAPPING_PARTOF_BRIDGEVETH == VerusConstants.MAPPING_PARTOF_BRIDGEVETH && destinationCurrency.flags & VerusConstants.MAPPING_ISBRIDGE_CURRENCY == VerusConstants.MAPPING_ISBRIDGE_CURRENCY && verusToERC20mapping[transfer.secondreserveid].flags & VerusConstants.MAPPING_PARTOF_BRIDGEVETH == VerusConstants.MAPPING_PARTOF_BRIDGEVETH, "Cannot convert non bridge reserves"); } else if (transfer.flags == (VerusConstants.VALID + VerusConstants.CONVERT + VerusConstants.IMPORT_TO_SOURCE)) { require(sendingCurrency.flags & VerusConstants.MAPPING_ISBRIDGE_CURRENCY == VerusConstants.MAPPING_ISBRIDGE_CURRENCY && destinationCurrency.flags & VerusConstants.MAPPING_PARTOF_BRIDGEVETH == VerusConstants.MAPPING_PARTOF_BRIDGEVETH && transfer.secondreserveid == address(0), "Cannot import non reserve to source"); } else if (transfer.flags == (VerusConstants.VALID + VerusConstants.CONVERT)) { require(sendingCurrency.flags & VerusConstants.MAPPING_PARTOF_BRIDGEVETH == VerusConstants.MAPPING_PARTOF_BRIDGEVETH && destinationCurrency.flags & VerusConstants.MAPPING_ISBRIDGE_CURRENCY == VerusConstants.MAPPING_ISBRIDGE_CURRENCY && transfer.secondreserveid == address(0), "Cannot convert non reserve"); } else { revert ("Invalid flag combination"); } return true; } function reverse(uint64 input) public pure returns (int64) { // swap bytes input = ((input & 0xFF00FF00FF00FF00) >> 8) | ((input & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs input = ((input & 0xFFFF0000FFFF0000) >> 16) | ((input & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs input = (input >> 32) | (input << 32); return int64(input); } function convertFromVerusNumber(uint256 a,uint8 decimals) public pure returns (uint256) { uint8 power = 10; //default value for 18 uint256 c = a; if(decimals > 8 ) { power = decimals - 8;// number of decimals in verus c = a * (10 ** power); }else if(decimals < 8){ power = 8 - decimals;// number of decimals in verus c = a / (10 ** power); } return c; } } ================================================ FILE: contracts/VerusBridge/SubmitImports.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusConstants.sol"; import "./Token.sol"; import "../Storage/StorageMaster.sol"; import "./CreateExports.sol"; import "./TokenManager.sol"; contract SubmitImports is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; address immutable DAI; address immutable MKR; constructor(address vETH, address Bridge, address Verus, address Dai, address Mkr){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; DAI = Dai; MKR = Mkr; } uint32 constant ELVCHOBJ_TXID_OFFSET = 32; uint32 constant ELVCHOBJ_NVINS_OFFSET = 45; uint32 constant FORKS_NOTARY_PROPOSER_POSITION = 96; uint32 constant TYPE_REFUND = 1; uint constant TYPE_BYTE_LOCATION_IN_UINT176 = 168; uint8 constant TYPE_REFUND_BYTES32_LOCATION = 244; enum Currency {VETH, DAI, VERUS, MKR} function initialize() external { } function buildReserveTransfer (uint64 value, uint176 sendTo, address sendingCurrency, uint64 fees, address feecurrencyid) private view returns (VerusObjects.CReserveTransfer memory) { VerusObjects.CReserveTransfer memory LPtransfer; LPtransfer.version = 1; LPtransfer.destination.destinationtype = uint8(sendTo >> TYPE_BYTE_LOCATION_IN_UINT176); LPtransfer.destcurrencyid = BRIDGE; LPtransfer.destsystemid = address(0); LPtransfer.secondreserveid = address(0); LPtransfer.flags = VerusConstants.VALID; LPtransfer.destination.destinationaddress = abi.encodePacked(uint160(sendTo)); LPtransfer.currencyvalue.currency = sendingCurrency; LPtransfer.feecurrencyid = feecurrencyid; LPtransfer.fees = fees; LPtransfer.currencyvalue.amount = value; return LPtransfer; } function sendBurnBackToVerus (uint64 sendAmount, address currency, uint64 fees) public view returns (VerusObjects.CReserveTransfer memory) { VerusObjects.CReserveTransfer memory LPtransfer; if (currency == VERUS) { LPtransfer = buildReserveTransfer(uint64(remainingLaunchFeeReserves - VerusConstants.verusTransactionFee), uint176(VerusConstants.VDXFID_VETH_BURN_ADDRESS), VERUS, VerusConstants.verusTransactionFee, VERUS ); } else if (currency == DAI) { LPtransfer = buildReserveTransfer(sendAmount, uint176(VerusConstants.VDXFID_VETH_BURN_ADDRESS), DAI, fees, DAI ); } LPtransfer.flags += VerusConstants.BURN_CHANGE_PRICE ; return LPtransfer; } function getImportFeeForReserveTransfer(address currency) public view returns (uint64) { uint64 feeShare; uint feeCalculation; uint reserves = claimableFees[bytes32(uint256(uint160(VerusConstants.VDXF_ETH_DAI_VRSC_LAST_RESERVES)))]; // Get the uint64 location in the uin256 word to calculate fees if (currency == DAI){ feeCalculation = uint(Currency.DAI) << 6; } else if (currency == MKR){ feeCalculation = uint(Currency.MKR) << 6; } else if (currency == VETH) { feeCalculation = uint(Currency.VETH) << 6; } else if (currency == VERUS) { return uint64(VerusConstants.VERUS_IMPORT_FEE); } else { // NOTE: Bridge.vETH currency is not supported. revert(); } feeCalculation = VerusConstants.VERUS_IMPORT_FEE_X2 * uint64(reserves >> feeCalculation); feeShare = uint64(feeCalculation / uint(uint64(reserves >> (uint(Currency.VERUS) << 6)))); return feeShare; } function _createImports(bytes calldata data) external returns(uint64, uint176) { uint256 gasleftStart = gasleft(); VerusObjects.CReserveTransferImport memory _import = abi.decode(data, (VerusObjects.CReserveTransferImport)); bytes32 txidfound; bytes memory elVchObj = _import.partialtransactionproof.components[0].elVchObj; uint32 nVins; assembly { txidfound := mload(add(elVchObj, ELVCHOBJ_TXID_OFFSET)) nVins := mload(add(elVchObj, ELVCHOBJ_NVINS_OFFSET)) } if (processedTxids[txidfound]) { revert(); } bool success; bytes memory returnBytes; // reverse 32bit endianess nVins = ((nVins & 0xFF00FF00) >> 8) | ((nVins & 0x00FF00FF) << 8); nVins = (nVins >> 16) | (nVins << 16); bytes32 hashOfTransfers; // [0..139]address of reward recipricent and [140..203]int64 fees uint64 fees; // [0..31]startheight [32..63]endheight [64..95]nIndex, [96..128] numberoftransfers packed into a uint128 uint128 CCEHeightsAndnIndex; hashOfTransfers = keccak256(_import.serializedTransfers); address verusProofAddress = contracts[uint(VerusConstants.ContractType.VerusProof)]; (success, returnBytes) = verusProofAddress.delegatecall(abi.encodeWithSignature("proveImports(bytes)", abi.encode(_import, hashOfTransfers))); require(success); uint176 exporter; (CCEHeightsAndnIndex, exporter) = abi.decode(returnBytes, (uint128, uint176)); //remove flags off exporter type. exporter = exporter & 0x0fffffffffffffffffffffffffffffffffffffffffff; isLastCCEInOrder(uint32(CCEHeightsAndnIndex)); // clear 4 bytes above first 64 bits, i.e. clear the nIndex 32 bit number, then convert to correct nIndex // Using the index for the proof (ouput of an export) - (header ( 2 * nvin )) == export output // NOTE: This depends on the serialization of the CTransaction header and the location of the vins being 45 bytes in. // NOTE: Also depends on it being a partial transaction proof, header = 1 CCEHeightsAndnIndex = (CCEHeightsAndnIndex & 0xffffffff00000000ffffffffffffffff) | (uint128(uint32(uint32(CCEHeightsAndnIndex >> 64) - (1 + (2 * nVins)))) << 64); setLastImport(txidfound, hashOfTransfers, CCEHeightsAndnIndex); // get the gasleft before calling the tokenmanager //returns success, refund addresses bytes & refund address array which is abi.encoded-ed, these are any refunds that didnt pay out. (success, returnBytes) = contracts[uint(VerusConstants.ContractType.TokenManager)].delegatecall(abi.encodeWithSelector(TokenManager.processTransactions.selector, _import.serializedTransfers, uint256(uint8(CCEHeightsAndnIndex >> 96)))); require(success); uint176[] memory refundAddresses; // returns refundaddresses bytes, fees and refundaddresses (returnBytes, fees, refundAddresses) = abi.decode(returnBytes, (bytes, uint64, uint176[])); // get the cceblockwidth of the cce from endheight - startheight and copy back into CCEHeightsAndnIndex CCEHeightsAndnIndex = (uint32(CCEHeightsAndnIndex >> 32) - uint32(CCEHeightsAndnIndex)); // calculate the fee to pay any refunds, and then pay to the refund addresses. calulateGasFees(gasleftStart, fees, refundAddresses, CCEHeightsAndnIndex, exporter); if (returnBytes.length > 0) { refund(returnBytes); } return (0,0); } function calulateGasFees(uint256 gasStart, uint64 fees, uint176[] memory refundAddresses, uint256 blockWidth, uint176 exporter) private { uint256 priceOfImports; // ETH price of the imports calculated from gas used. uint64 notaryFees; // fees to pay to notaries uint64 blockDivisor; // ratio adjustment when traffic is high uint64 minTxesForRefund; // minimum number of transactions for a refund uint64 feeRefunds; uint64 processorsFees; // fees shared out between notaries / exporters / proposers. // Using the gas used gives us an indication of how much the transaction will cost. // i.e. the gas used to do 2 * notaryimports + fixedcost for submitimport + (gas to process the tx payements) priceOfImports = uint256((gasStart - gasleft()) + VerusConstants.GAS_BASE_COST_FOR_NOTARYS + (refundAddresses.length * VerusConstants.GAS_BASE_COST_FOR_REFUND_PAYOUTS)) * tx.gasprice; notaryFees = uint64(((priceOfImports * 14) / 10) / VerusConstants.SATS_TO_WEI_STD); // Use a Buffer of 40% for notary fees. if (fees > (notaryFees + (notaryFees >> 4))){ blockDivisor = 20; minTxesForRefund = VerusConstants.MINIMUM_TRANSACTIONS_FOR_REFUNDS; if (blockWidth > 1) { blockDivisor = 10; minTxesForRefund = VerusConstants.MINIMUM_TRANSACTIONS_FOR_REFUNDS_HALF; } if (refundAddresses.length > minTxesForRefund) { processorsFees = fees - notaryFees; fees = notaryFees; feeRefunds = uint64((processorsFees / refundAddresses.length) * (refundAddresses.length - minTxesForRefund)); feeRefunds = feeRefunds - (feeRefunds / blockDivisor); processorsFees = processorsFees - feeRefunds; feeRefunds = feeRefunds / uint64(refundAddresses.length); // Divide by number of transactions to get refund share per number of transfers. for(uint i = 0; i < refundAddresses.length; i++) { bytes32 feeRefundAddress; feeRefundAddress = bytes32(uint256(refundAddresses[i])); if (feeRefundAddress != bytes32(0)) { feeRefundAddress |= bytes32(uint256(TYPE_REFUND) << TYPE_REFUND_BYTES32_LOCATION); refunds[feeRefundAddress][VETH] += feeRefunds; } else { processorsFees += feeRefunds; } } } else { processorsFees = fees - notaryFees; fees = notaryFees; } } setClaimableFees(fees, exporter, processorsFees); } function refund(bytes memory refundAmount) private { if (refundAmount.length < 50) return; //early return if no refunds. // Note each refund is 50 bytes = 22bytes(uint176) + uint64 + uint160 (currency) for(uint i = 0; i < refundAmount.length; i = i + 50) { uint176 verusAddress; uint64 amount; address currency; assembly { verusAddress := mload(add(add(refundAmount, 22), i)) amount := mload(add(add(refundAmount, 30), i)) currency := mload(add(add(refundAmount, 50), i)) } bytes32 refundAddress; // The leftmost byte is the TYPE_REFUND; refundAddress = bytes32(uint256(verusAddress) | uint256(TYPE_REFUND) << TYPE_REFUND_BYTES32_LOCATION); refunds[refundAddress][currency] += amount; } } function setLastImport(bytes32 processedTXID, bytes32 hashofTXs, uint128 CCEheightsandTXNum ) private { processedTxids[processedTXID] = true; // lastTxIdImport = processedTXID; lastImportInfo[VerusConstants.SUBMIT_IMPORTS_LAST_TXID] = VerusObjects.lastImportInfo(hashofTXs, processedTXID, uint32(CCEheightsandTXNum >> 64), uint32(CCEheightsandTXNum >> 32)); } function isLastCCEInOrder(uint32 height) private view { if ((lastImportInfo[VerusConstants.SUBMIT_IMPORTS_LAST_TXID].height + 1) == height) { return; } else if (lastImportInfo[VerusConstants.SUBMIT_IMPORTS_LAST_TXID].hashOfTransfers == bytes32(0)) { return; } else{ revert(); } } function getReadyExportsByRange(uint _startBlock,uint _endBlock) external view returns(VerusObjects.CReserveTransferSetCalled[] memory returnedExports){ uint outputSize; uint heights = _startBlock; bool loop = cceLastEndHeight > 0; if(!loop) return returnedExports; while(loop){ heights = _readyExports[heights].endHeight + 1; if (heights > _endBlock || heights == 1) { break; } outputSize++; } returnedExports = new VerusObjects.CReserveTransferSetCalled[](outputSize); VerusObjects.CReserveTransferSet memory tempSet; heights = _startBlock; for (uint i = 0; i < outputSize; i++) { tempSet = _readyExports[heights]; returnedExports[i] = VerusObjects.CReserveTransferSetCalled(tempSet.exportHash, tempSet.prevExportHash, uint64(heights), tempSet.endHeight, tempSet.transfers); heights = tempSet.endHeight + 1; } return returnedExports; } function setClaimableFees(uint64 notaryFees, uint176 exporter, uint64 processorsFees) private { uint64 feeShare; feeShare = processorsFees / 3; claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] += (notaryFees + feeShare); if (processorsFees > 0) { bytes memory proposerBytes = bestForks[0]; uint176 proposer; assembly { proposer := mload(add(proposerBytes, FORKS_NOTARY_PROPOSER_POSITION)) } setClaimedFees(bytes32(uint256(proposer)), feeShare); // 1/3 to proposer setClaimedFees(bytes32(uint256(exporter)), feeShare + (feeShare % 3)); // any remainder from main division goes to exporter } } function setClaimedFees(bytes32 _address, uint256 fees) private { claimableFees[_address] += fees; } function claimfees() public { uint256 claimAmount; if ((claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] * VerusConstants.SATS_TO_WEI_STD) > VerusConstants.CLAIM_NOTARY_FEE_THRESHOLD) { uint256 txReimburse; // truncate reimburse amount txReimburse = ((tx.gasprice * VerusConstants.NOTARY_CLAIM_TX_GAS_COST) / VerusConstants.SATS_TO_WEI_STD) * VerusConstants.SATS_TO_WEI_STD; if (claimableFees[bytes32(0)] > 0) { // When there is no proposer fees are sent to bytes(0) claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] += claimableFees[bytes32(0)]; claimableFees[bytes32(0)] = 0; } claimAmount = claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] - (txReimburse / VerusConstants.SATS_TO_WEI_STD); claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] = 0; uint256 claimShare; claimShare = claimAmount / notaries.length; for (uint i = 0; i < notaries.length; i++) { if (notaryAddressMapping[notaries[i]].state == VerusConstants.NOTARY_VALID) { claimAmount -= claimShare; payable(notaryAddressMapping[notaries[i]].main).transfer(claimShare * VerusConstants.SATS_TO_WEI_STD); } } claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] = claimAmount; payable(msg.sender).transfer(txReimburse); } } function claimRefund(uint176 verusAddress, address currency) external payable returns (uint) { uint256 refundAmount; bytes32 refundAddressFormatted; VerusObjects.CReserveTransfer memory LPtransfer; bool success; refundAddressFormatted = bytes32(uint256(verusAddress) | uint256(TYPE_REFUND) << TYPE_REFUND_BYTES32_LOCATION); refundAmount = refunds[refundAddressFormatted][currency]; require (bridgeConverterActive && refundAmount > 0 && verusAddress != uint176(0)); delete refunds[refundAddressFormatted][currency]; uint64 fees; address feeCurrency; if (currency != VETH && currency != DAI && currency != VERUS && currency != MKR) { fees = getImportFeeForReserveTransfer(VETH); if (msg.value < (fees * VerusConstants.SATS_TO_WEI_STD)) { revert(); } //The user may have put too much in, so update fees for correct accounting. fees = uint64(msg.value / VerusConstants.SATS_TO_WEI_STD); feeCurrency = VETH; } else { fees = getImportFeeForReserveTransfer(currency); require (refundAmount > fees); feeCurrency = currency; } LPtransfer = buildReserveTransfer(uint64(refundAmount), verusAddress, currency, fees, feeCurrency); (success, ) = contracts[uint(VerusConstants.ContractType.CreateExport)] .delegatecall(abi.encodeWithSelector(CreateExports.externalCreateExportCallPayable.selector, abi.encode(LPtransfer, false))); require(success); return 0; } // Caclulates the amount of DAI, MKR or VERUS to reimburse the user for the transaction fee. function getTxFeeReimbursement (address currency) private view returns (uint64) { uint256 txReimburse; // keep the Reimburse value in wei until end for accuracy txReimburse = (tx.gasprice * VerusConstants.REFUND_FEE_REIMBURSE_GAS_AMOUNT); uint reserves = claimableFees[bytes32(uint256(uint160(VerusConstants.VDXF_ETH_DAI_VRSC_LAST_RESERVES)))]; uint feeCalculation; // multiply the ETH amount by the reserve of ETH // Get the uint64 location in the uin256 word to calculate fees if (currency == DAI){ feeCalculation = uint(Currency.DAI) << 6; } else if (currency == MKR){ feeCalculation = uint(Currency.MKR) << 6; } else if (currency == VERUS) { feeCalculation = uint(Currency.VERUS) << 6; } else { // NOTE: Bridge.vETH currency is not supported. revert(); } // multiply the ETH amount by the reserves of the chosen currency txReimburse = txReimburse * uint64(reserves >> feeCalculation); txReimburse = (txReimburse / uint(uint64(reserves >> (uint(Currency.VETH) << 6)))) / VerusConstants.SATS_TO_WEI_STD; return uint64(txReimburse); } function sendfees(bytes32 publicKeyX, bytes32 publicKeyY) public { require(bridgeConverterActive); uint8 leadingByte; uint256 claimant; uint64 feeShare; leadingByte = (uint256(publicKeyY) & 1) == 1 ? 0x03 : 0x02; claimant = uint160(ripemd160(abi.encodePacked(sha256(abi.encodePacked(leadingByte, publicKeyX))))); claimant |= (uint256(0x0214) << VerusConstants.UINT160_BITS_SIZE); // is Claimient type R address and 20 bytes. if ((claimableFees[bytes32(claimant)] > VerusConstants.verusvETHTransactionFee) && msg.sender == address(uint160(uint256(keccak256(abi.encodePacked(publicKeyX, publicKeyY)))))) { feeShare = uint64(claimableFees[bytes32(claimant)]); claimableFees[bytes32(claimant)] = 0; payable(msg.sender).transfer(feeShare * VerusConstants.SATS_TO_WEI_STD); return; } if ((claimableFees[publicKeyX] > (VerusConstants.verusvETHTransactionFee << 1))) { require(publicKeyX[10] == 0x04); require(claimableFees[publicKeyX] > VerusConstants.verusvETHTransactionFee); feeShare = uint64(claimableFees[publicKeyX]) - VerusConstants.verusvETHTransactionFee; claimableFees[publicKeyX] = 0; VerusObjects.CReserveTransfer memory LPtransfer; LPtransfer = buildReserveTransfer(feeShare, uint176(uint256(publicKeyX)), VETH, VerusConstants.verusvETHTransactionFee, VETH); (bool success, ) = contracts[uint(VerusConstants.ContractType.CreateExport)] .delegatecall(abi.encodeWithSelector(CreateExports.externalCreateExportCallPayable.selector, abi.encode(LPtransfer, false))); require(success); } } function convertFromVerusNumber(uint256 a,uint8 decimals) private pure returns (uint256) { uint8 power = 10; //default value for 18 uint256 c = a; if(decimals > 8 ) { power = decimals - 8;// number of decimals in verus c = a * (10 ** power); }else if(decimals < 8){ power = 8 - decimals;// number of decimals in verus c = a / (10 ** power); } return c; } } ================================================ FILE: contracts/VerusBridge/Token.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import '@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol'; contract Token is ERC20 { address private owner; constructor(string memory _name, string memory _symbol) ERC20(_name,_symbol) { owner = msg.sender; } function mint(address to, uint256 amount) public { require(msg.sender == owner,"Only the contract owner can Mint Tokens"); _mint(to, amount); } function burn(uint256 amount) public virtual { require(msg.sender == owner,"Only the contract owner can Burn Tokens"); _burn(_msgSender(), amount); } function changeowner(address newOwner) public virtual { require(msg.sender == owner,"Only the contract owner can update the owner"); owner = newOwner; } } contract VerusNft is ERC721URIStorage { address private owner; constructor() ERC721('VerusNFT', 'vNFT') { owner = msg.sender; } function mint(address tokenId, string memory inTokenURI, address recipient) public { require(msg.sender == owner,"Only the contract owner can Mint NFTS"); _mint(recipient, uint256(uint160(tokenId))); _setTokenURI(uint256(uint160(tokenId)), inTokenURI); } function burn(uint256 tokenId) public { require(msg.sender == owner,"Only the contract owner can Burn NFTS"); _burn(tokenId); } function changeowner(address newOwner) public virtual { require(msg.sender == owner,"Only the contract owner can update the owner"); owner = newOwner; } } ================================================ FILE: contracts/VerusBridge/TokenManager.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "./Token.sol"; import "../Libraries/VerusConstants.sol"; import "../Libraries/VerusObjects.sol"; import {VerusSerializer} from "../VerusBridge/VerusSerializer.sol"; import "../VerusBridge/CreateExports.sol"; import "../VerusBridge/UpgradeManager.sol"; import "../VerusBridge/SubmitImports.sol"; import "../Libraries/VerusObjectsCommon.sol"; import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol"; import "../Storage/StorageMaster.sol"; import "./VerusCrossChainExport.sol"; contract TokenManager is VerusStorage { address immutable VETH; address immutable VERUS; address immutable DAIERC20ADDRESS; uint8 constant SEND_FAILED = 1; uint8 constant SEND_SUCCESS = 2; uint8 constant SEND_SUCCESS_ERC1155 = 3; uint8 constant SEND_SUCCESS_ERC721 = 4; uint8 constant SEND_SUCCESS_ERC20_MINTED = 5; uint8 constant SEND_SUCCESS_ETH = 6; bytes4 constant ERC20_SEND_SELECTOR = ERC20.transfer.selector ; bytes4 constant ERC20_MINT_SELECTOR = Token.mint.selector ; bytes4 constant ERC721_SEND_SELECTOR = IERC721.transferFrom.selector; bytes4 constant ERC1155_SEND_SELECTOR = IERC1155.safeTransferFrom.selector; constructor(address vETH, address, address Verus, address DaiERC20Address){ VETH = vETH; VERUS = Verus; DAIERC20ADDRESS = DaiERC20Address; } function initialize() external { } function launchToken(VerusObjects.PackedCurrencyLaunch[] memory _tx) private { for (uint j = 0; j < _tx.length; j++) { // If the iaddress is already mapped or the iaddress is null skip token register if (verusToERC20mapping[_tx[j].iaddress].flags > 0 || _tx[j].iaddress == address(0)) continue; VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]).checkIAddress(_tx[j]); string memory outputName; // Only adjust the name of the token if it is a Ethereum owned token and it not a ERC1155 NFT if (_tx[j].flags & (VerusConstants.MAPPING_ETHEREUM_OWNED | VerusConstants.MAPPING_ERC1155_ERC_DEFINITION | VerusConstants.MAPPING_ERC1155_NFT_DEFINITION) == VerusConstants.MAPPING_ETHEREUM_OWNED) { (bool success, bytes memory result) = _tx[j].ERCContract.call{gas:30000}(abi.encodeWithSignature("name()")); if (success) { outputName = abi.decode(result, (string)); } else { outputName = "..."; } outputName = string(abi.encodePacked("[", outputName, "] as ")); } outputName = string(abi.encodePacked(outputName, _tx[j].name)); if (_tx[j].parent != VERUS) { outputName = string(abi.encodePacked(outputName, ".", verusToERC20mapping[_tx[j].parent].name)); } recordToken(_tx[j].iaddress, _tx[j].ERCContract, outputName, _tx[j].name, uint8(_tx[j].flags), _tx[j].tokenID); } } function recordToken( address _iaddress, address ethContractAddress, string memory name, string memory ticker, uint8 flags, uint256 tokenID ) public { address ERCContract; if (flags & VerusConstants.MAPPING_VERUS_OWNED == VerusConstants.MAPPING_VERUS_OWNED) { if (flags & VerusConstants.MAPPING_ERC20_DEFINITION == VerusConstants.MAPPING_ERC20_DEFINITION) { Token t = new Token(name, ticker); ERCContract = address(t); } else if (flags & VerusConstants.MAPPING_ERC721_NFT_DEFINITION == VerusConstants.MAPPING_ERC721_NFT_DEFINITION) { ERCContract = verusToERC20mapping[tokenList[VerusConstants.NFT_POSITION]].erc20ContractAddress; tokenID = uint256(uint160(_iaddress)); //tokenID is the i address } } else { ERCContract = ethContractAddress; } tokenList.push(_iaddress); // TokenIndex is used for accounting of ERC20, ERC721, ERC1155 so allways start at 0. verusToERC20mapping[_iaddress] = VerusObjects.mappedToken(ERCContract, flags, 0, name, tokenID); } function processTransactions(bytes calldata serializedTransfers, uint256 numberOfTransfers) external returns (bytes memory refundsData, uint256 fees, uint176[] memory refundAddresses) { VerusObjects.PackedSend[] memory transfers; VerusObjects.PackedCurrencyLaunch[] memory launchTxs; uint32 counter; (transfers, launchTxs, counter, refundAddresses) = VerusSerializer(contracts[uint(VerusConstants.ContractType.VerusSerializer)]) .deserializeTransfers(serializedTransfers, uint8(numberOfTransfers)); // Only two currency launches are allowed per CCE, so use a third one to store fees, as function is to large. fees = uint64(launchTxs[2].tokenID); refundsData = importTransactions(transfers, refundAddresses); // 32bit counter is split into two 16bit values, the first 16bits is the number of transactions, the second 16bits is the number of currency launches if (uint8(counter >> 24) > 0) { launchToken(launchTxs); } //return and refund any failed transactions return (refundsData, fees, refundAddresses); } function importTransactions(VerusObjects.PackedSend[] memory trans, uint176[] memory refundAddresses) private returns (bytes memory refundsData){ VerusObjects.mappedToken memory tempToken; for(uint256 i = 0; i < trans.length; i++) { uint64 sendAmount; address destinationAddress; address currencyiAddress; uint32 result; sendAmount = uint64(trans[i].currencyAndAmount >> VerusConstants.UINT160_BITS_SIZE); destinationAddress = address(uint160(trans[i].destinationAndFlags)); tempToken = verusToERC20mapping[address(uint160(trans[i].currencyAndAmount))]; currencyiAddress = address(uint160(trans[i].currencyAndAmount)); if (currencyiAddress == VETH) { // NOTE: Send limits gas so cannot pay to contract addresses with fallback functions. (bool success, ) = destinationAddress.call{value: (sendAmount * VerusConstants.SATS_TO_WEI_STD), gas: 100000}(""); result = success ? SEND_SUCCESS_ETH : SEND_FAILED; } else if (tempToken.flags & VerusConstants.MAPPING_ERC721_NFT_DEFINITION == VerusConstants.MAPPING_ERC721_NFT_DEFINITION && tempToken.flags & VerusConstants.MAPPING_VERUS_OWNED == VerusConstants.MAPPING_VERUS_OWNED) { VerusNft t = VerusNft(tempToken.erc20ContractAddress); t.mint(currencyiAddress, tempToken.name, destinationAddress); // Do nothing after minted. result = 0; } else if (tempToken.flags & VerusConstants.MAPPING_ERC20_DEFINITION == VerusConstants.MAPPING_ERC20_DEFINITION) { // if the ERC20 type is verus owned then mint the currency to the destination address, else transfer the currency to the destination address. result = uint32((tempToken.flags & VerusConstants.MAPPING_VERUS_OWNED == VerusConstants.MAPPING_VERUS_OWNED) ? uint32(ERC20_MINT_SELECTOR) : uint32(ERC20_SEND_SELECTOR)); } else if (tempToken.flags & VerusConstants.MAPPING_ERC721_NFT_DEFINITION == VerusConstants.MAPPING_ERC721_NFT_DEFINITION) { result = uint32(ERC721_SEND_SELECTOR); } else if (tempToken.flags & VerusConstants.MAPPING_ERC1155_NFT_DEFINITION == VerusConstants.MAPPING_ERC1155_NFT_DEFINITION || tempToken.flags & VerusConstants.MAPPING_ERC1155_ERC_DEFINITION == VerusConstants.MAPPING_ERC1155_ERC_DEFINITION) { result = uint32(ERC1155_SEND_SELECTOR); } // if result is a sector then use it to make the call in the sendCurrencyToETHAddress function, else call is already made. if(result > SEND_SUCCESS_ETH) { result = sendCurrencyToETHAddress(tempToken.erc20ContractAddress, destinationAddress, sendAmount, result, tempToken.tokenID); } if (result == SEND_FAILED && sendAmount > 0) { refundsData = abi.encodePacked(refundsData, refundAddresses[i], sendAmount, currencyiAddress); } else if (result == SEND_SUCCESS) { // TokenIndex used for ERC20, ERC721 & ERC1155 Acounting so decrement holdings if successful verusToERC20mapping[currencyiAddress].tokenIndex -= sendAmount; } } } // Returns true if successful transfer function sendCurrencyToETHAddress(address tokenERCAddress, address destinationAddress, uint256 sendAmount, uint32 selector, uint256 TokenId ) private returns (uint8){ bytes memory data; uint256 amount; bool success; if (selector == uint32(ERC20_MINT_SELECTOR) || selector == uint32(ERC20_SEND_SELECTOR)) { (success, data) = tokenERCAddress.call{gas: 30000}(abi.encodeWithSelector(ERC20.decimals.selector)); if(!success) { return SEND_FAILED; } amount = convertFromVerusNumber(sendAmount, abi.decode(data, (uint8))); } if(tokenERCAddress == DAIERC20ADDRESS) { address crossChainExportAddress = contracts[uint(VerusConstants.ContractType.VerusCrossChainExport)]; (success,) = crossChainExportAddress.delegatecall(abi.encodeWithSelector(VerusCrossChainExport.exit.selector, destinationAddress, amount)); return success ? SEND_SUCCESS : SEND_FAILED; } else if(selector == uint32(ERC20_MINT_SELECTOR) || selector == uint32(ERC20_SEND_SELECTOR)) { data = abi.encodeWithSelector(bytes4(selector), destinationAddress, amount); } else if(selector == uint32(ERC721_SEND_SELECTOR)) { data = abi.encodeWithSelector(bytes4(selector), address(this), destinationAddress, TokenId); } else if(selector == uint32(ERC1155_SEND_SELECTOR)) { data = abi.encodeWithSelector(bytes4(selector), address(this), destinationAddress, TokenId, sendAmount, ""); } (success, data) = tokenERCAddress.call{gas: 100000}(data); if (!success || (data.length >= 32 && abi.decode(data, (uint256)) == 0)) { return SEND_FAILED; } return selector == uint32(ERC20_MINT_SELECTOR) ? SEND_SUCCESS_ERC20_MINTED : SEND_SUCCESS; } function convertFromVerusNumber(uint256 a, uint8 decimals) internal pure returns (uint256 c) { if(decimals > 8 ) { uint8 power = decimals - 8;// number of decimals in verus c = a * (10 ** power); }else if(decimals < 8){ uint8 power = 8 - decimals;// number of decimals in verus c = a / (10 ** power); } else { c = a; } return c; } } ================================================ FILE: contracts/VerusBridge/UpgradeManager.sol ================================================ // SPDX-License-Identifier: MIT pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusObjectsNotarization.sol"; import "../Storage/StorageMaster.sol"; import "./dsrinterface.sol"; contract UpgradeManager is VerusStorage { uint8 constant TYPE_CONTRACT = 1; uint8 constant TYPE_REVOKE = 2; uint8 constant TYPE_RECOVER = 3; uint8 constant TYPE_AUTO_REVOKE = 4; uint8 constant NUM_ADDRESSES_FOR_REVOKE = 2; uint8 constant PENDING = 1; uint8 constant COMPLETE = 2; uint8 constant UPGRADE_IN_PROCESS = 3; uint8 constant ERROR = 4; uint8 constant REQUIREDAMOUNTOFVOTES = 100; uint8 constant WINNINGAMOUNT = 51; event contractUpdated(bool); function initialize() external { rollingVoteIndex = VerusConstants.DEFAULT_INDEX_VALUE; } function upgradeContracts(bytes calldata data) external payable returns (uint8) { VerusObjects.upgradeInfo memory _newContractPackage; (_newContractPackage) = abi.decode(data, (VerusObjects.upgradeInfo)); checkValidContractUpgrade(_newContractPackage); return PENDING; } function checkValidContractUpgrade(VerusObjects.upgradeInfo memory _newContractPackage) private { bytes memory be; address contractsHash; uint256 contractsLength; contractsLength = contracts.length; require(saltsUsed[_newContractPackage.salt] == false, "salt Already used"); saltsUsed[_newContractPackage.salt] = true; require(contractsLength == _newContractPackage.contracts.length, "Input contracts wrong length"); for (uint i = 0; i < contractsLength; i++) { be = abi.encodePacked(be, _newContractPackage.contracts[i]); } be = abi.encodePacked(be, uint8(_newContractPackage.upgradeType), _newContractPackage.salt); contractsHash = address(uint160(uint256(keccak256(be)))); require(contractsHash != address(0), "Invalid contract hash"); // If the vote on the hash has already been used, then we can't use it again. if (getVoteCount(contractsHash) > 25 && successfulVoteHashes[contractsHash] != VerusConstants.MAX_UINT256) { // Set the upgrade hash address as used successfulVoteHashes[contractsHash] = VerusConstants.MAX_UINT256; // reset the rolling vote index to default viewer. rollingVoteIndex = VerusConstants.DEFAULT_INDEX_VALUE; for (uint j = 0; j < uint(VerusConstants.NUMBER_OF_CONTRACTS); j++) { if (contracts[j] != _newContractPackage.contracts[j]) { contracts[j] = _newContractPackage.contracts[j]; //NOTE: Upgraded contracts must have a initialize() function to be present! (bool success,) = _newContractPackage.contracts[j].delegatecall{gas: 3000000}(abi.encodeWithSignature("initialize()")); require(success); } } } else { revert(); } } function bytesToString (bytes memory input) private pure returns (bytes memory output) { bytes memory _string = new bytes(input.length * 2); bytes memory HEX = "0123456789abcdef"; for(uint i = 0; i < input.length; i++) { _string[i*2] = HEX[uint8(input[i] >> 4)]; _string[1+i*2] = HEX[uint8(input[i] & 0x0f)]; } return _string; } function getVoteCount(address contractsHash) public view returns (uint) { uint countOfAgreedVotes; uint256 votesLength; votesLength = VerusConstants.VOTE_LENGTH; for(uint i = 0; i < votesLength; i++) { if (contractsHash == rollingUpgradeVotes[i]) countOfAgreedVotes++; } return countOfAgreedVotes; } function recoverSigner(bytes32 _h, uint8 _v, bytes32 _r, bytes32 _s) private pure returns (address) { return ecrecover(_h, _v, _r, _s); } function writeCompactSize(uint newNumber) public pure returns(bytes memory) { bytes memory output; if (newNumber < uint8(253)) { output = abi.encodePacked(uint8(newNumber)); } else if (newNumber <= 0xFFFF) { output = abi.encodePacked(uint8(253),uint8(newNumber & 0xff),uint8(newNumber >> 8)); } else if (newNumber <= 0xFFFFFFFF) { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24)); } else { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24),uint8(newNumber >> 32),uint8(newNumber >> 40),uint8(newNumber >> 48),uint8(newNumber >> 56)); } return output; } } ================================================ FILE: contracts/VerusBridge/VerusCrossChainExport.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusConstants.sol"; import "../VerusBridge/VerusSerializer.sol"; import "../Storage/StorageMaster.sol"; import "./dsrinterface.sol"; contract VerusCrossChainExport is VerusStorage { VerusObjects.CCurrencyValueMap[] currencies; VerusObjects.CCurrencyValueMap[] fees; address immutable VETH; address immutable BRIDGE; address immutable VERUS; address immutable DAIERC20; address immutable pot; address immutable daiJoin; constructor(address vETH, address Bridge, address Verus, address daiERC20, address potAddress, address daiJoinAddress) { VETH = vETH; BRIDGE = Bridge; VERUS = Verus; DAIERC20 = daiERC20; pot = potAddress; daiJoin = daiJoinAddress; } function initialize() external { VatLike vat = VatLike(PotLike(pot).vat()); vat.hope(daiJoin); vat.hope(pot); IERC20(DAIERC20).approve(daiJoin, uint256(int256(-1))); } uint256 constant RAY = 10 ** 27; function add(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x + y) >= x); } function sub(uint256 x, uint256 y) internal pure returns (uint256 z) { require((z = x - y) <= x); } function mul(uint256 x, uint256 y) internal pure returns (uint256 z) { require(y == 0 || (z = x * y) / y == x); } function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) { // always rounds down z = mul(x, y) / RAY; } function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) { // always rounds down z = mul(x, RAY) / y; } function rdivup(uint256 x, uint256 y) internal pure returns (uint256 z) { // always rounds up z = add(mul(x, RAY), sub(y, 1)) / y; } function quickSort(VerusObjects.CCurrencyValueMap[] storage currency, int left, int right) private { int i = left; int j = right; if (i == j) return; uint160 pivot = uint160(currency[uint256(left + (right - left) / 2)].currency); while (i <= j) { while (uint160(currency[uint256(i)].currency) < pivot) i++; while (pivot < uint160(currency[uint256(j)].currency)) j--; if (i <= j) { VerusObjects.CCurrencyValueMap memory temp = currency[uint256(i)]; currency[uint256(i)] = currency[uint256(j)]; currency[uint256(j)] = temp; i++; j--; } } if (left < j) quickSort(currency, left, j); if (i < right) quickSort(currency, i, right); } function inCurrencies(address checkCurrency) private view returns(uint256){ for(uint256 i = 0; i < currencies.length; i++){ if(currencies[i].currency == checkCurrency) return i + 1; } return 0; } function inFees(address checkFeesCurrency) private view returns(uint256){ for(uint256 i = 0; i < fees.length; i++){ if(fees[i].currency == checkFeesCurrency) return i + 1; } return 0; } function generateCCE(bytes memory bytesin) external returns(bytes memory){ (VerusObjects.CReserveTransfer[] memory transfers, bool bridgeReady, uint64 startheight, uint64 endheight, address verusSerializer) = abi.decode(bytesin, (VerusObjects.CReserveTransfer[], bool, uint64, uint64, address)); VerusObjects.CCrossChainExport memory workingCCE; //create a hash of the transfers and then bytes memory serializedTransfers = VerusSerializer(verusSerializer).serializeCReserveTransfers(transfers, false); bytes32 hashedTransfers = keccak256(serializedTransfers); //create the Cross ChainExport to then serialize and hash workingCCE.version = 1; workingCCE.flags = 2; workingCCE.sourceheightstart = startheight; workingCCE.sourceheightend = endheight; workingCCE.sourcesystemid = VETH; workingCCE.hashtransfers = hashedTransfers; workingCCE.destinationsystemid = VERUS; if (bridgeReady) { workingCCE.destinationcurrencyid = BRIDGE; //NOTE:transfers are bundled by type } else { workingCCE.destinationcurrencyid = VERUS; } workingCCE.numinputs = uint32(transfers.length); //loop through the array and create totals of the amounts and fees uint256 currencyExists; uint256 feeExistsInTotals; uint256 feeExists; for(uint i = 0; i < transfers.length; i++){ currencyExists = inCurrencies(transfers[i].currencyvalue.currency); if(currencyExists > 0){ currencies[currencyExists - 1].amount += transfers[i].currencyvalue.amount; } else { currencies.push(VerusObjects.CCurrencyValueMap(transfers[i].currencyvalue.currency,transfers[i].currencyvalue.amount)); } //add the fees into the totalamounts too feeExistsInTotals = inCurrencies(transfers[i].feecurrencyid); if(feeExistsInTotals > 0){ currencies[feeExistsInTotals - 1].amount += transfers[i].fees; } else { currencies.push(VerusObjects.CCurrencyValueMap(transfers[i].feecurrencyid, transfers[i].fees)); } feeExists = inFees(transfers[i].feecurrencyid); if(feeExists > 0){ fees[feeExists - 1].amount += transfers[i].fees; } else { fees.push(VerusObjects.CCurrencyValueMap(transfers[i].feecurrencyid, transfers[i].fees)); } } quickSort(currencies, int(0), int(currencies.length - 1)); //maps in the daemon are sorted, sort array. quickSort(fees, int(0), int(fees.length - 1)); workingCCE.totalamounts = currencies; workingCCE.totalfees = fees; VerusObjects.CCurrencyValueMap memory totalburnedCCVM = VerusObjects.CCurrencyValueMap(address(0), 0); workingCCE.totalburned = new VerusObjects.CCurrencyValueMap[](1); workingCCE.totalburned[0] = totalburnedCCVM; //workingCCE.rewardaddress is left empty as it is serialized to 0x0000 workingCCE.firstinput = 1; // clear the arrays delete currencies; delete fees; return VerusSerializer(verusSerializer).serializeCCrossChainExport(workingCCE); } function daiBalance() external returns (uint256 wad) { uint256 chi = (block.timestamp > PotLike(pot).rho()) ? PotLike(pot).drip() : PotLike(pot).chi(); wad = rmul(chi, claimableFees[VerusConstants.VDXFID_DAI_DSR_SUPPLY]); } // wad is denominated in dai function join(uint256 wad) external payable { uint256 chi = (block.timestamp > PotLike(pot).rho()) ? PotLike(pot).drip() : PotLike(pot).chi(); uint256 pie = rdiv(wad, chi); claimableFees[VerusConstants.VDXFID_DAI_DSR_SUPPLY] = add(claimableFees[VerusConstants.VDXFID_DAI_DSR_SUPPLY], pie); claimableFees[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS] = add(claimableFees[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS], wad); JoinLike(daiJoin).join(address(this), wad); PotLike(pot).join(pie); } // wad is denominated in dai function exit(address dst, uint256 wad) external { uint256 chi = (block.timestamp > PotLike(pot).rho()) ? PotLike(pot).drip() : PotLike(pot).chi(); uint256 pie = rdivup(wad, chi); claimableFees[VerusConstants.VDXFID_DAI_DSR_SUPPLY] = sub(claimableFees[VerusConstants.VDXFID_DAI_DSR_SUPPLY], pie); PotLike(pot).exit(pie); uint256 amt = rmul(chi, pie); claimableFees[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS] = sub(claimableFees[VerusConstants.VDXF_SYSTEM_DAI_HOLDINGS], amt) ; JoinLike(daiJoin).exit(dst, amt); } } ================================================ FILE: contracts/VerusBridge/VerusSerializer.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusObjectsNotarization.sol"; import "../Libraries/VerusConstants.sol"; contract VerusSerializer { uint constant ETH_ADDRESS_SIZE_BYTES = 20; uint32 constant CCC_PREFIX_TO_OPTIONS = 3 + 4; // already starts on the byte so 3 first uint32 constant VERUS_ID_LENGTH = 20; uint32 constant CCC_NATIVE_OFFSET = 4 + 4 + 1; uint32 constant CCC_TOKENID_OFFSET = 32; uint32 constant ETH_SEND_GATEWAY_AND_AUX_DEST = 20 + 20 + 8 + 4 + 20; uint32 constant TRANSFER_GATEWAYSKIP = 56; //skip gatewayid, gatewaycode + fees uint8 constant FLAG_MASK = 192; // 11000000 function initialize() external { } function readCompactSizeLE(bytes memory incoming, uint32 offset) public pure returns(VerusObjectsCommon.UintReader memory) { uint8 oneByte; assembly { oneByte := mload(add(incoming, offset)) } offset++; if (oneByte < 253) { return VerusObjectsCommon.UintReader(offset, oneByte); } else if (oneByte == 253) { offset++; uint16 twoByte; assembly { twoByte := mload(add(incoming, offset)) } return VerusObjectsCommon.UintReader(offset + 1, ((twoByte << 8) & 0xffff) | twoByte >> 8); } return VerusObjectsCommon.UintReader(offset, 0); } function writeVarInt(uint64 incoming) public pure returns(bytes memory) { bytes1 inProgress; bytes memory output; uint len = 0; while(true){ inProgress = bytes1(uint8(incoming & 0x7f) | (len!=0 ? 0x80:0x00)); output = abi.encodePacked(inProgress,output); if(incoming <= 0x7f) break; incoming = (incoming >> 7) -1; len++; } return output; } function writeCompactSize(uint newNumber) public pure returns(bytes memory) { bytes memory output; if (newNumber < uint8(253)) { output = abi.encodePacked(uint8(newNumber)); } else if (newNumber <= 0xFFFF) { output = abi.encodePacked(uint8(253),uint8(newNumber & 0xff),uint8(newNumber >> 8)); } else if (newNumber <= 0xFFFFFFFF) { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24)); } else { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24),uint8(newNumber >> 32),uint8(newNumber >> 40),uint8(newNumber >> 48),uint8(newNumber >> 56)); } return output; } //serialize functions function serializeUint16(uint16 number) public pure returns(uint16){ number = (number << 8) | (number >> 8) ; return number; } function serializeUint32(uint32 number) public pure returns(uint32){ // swap bytes number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8); number = (number >> 16) | (number << 16); return number; } function serializeInt16(int16 number) public pure returns(int16){ number = (number << 8) | (number >> 8) ; return number; } function serializeInt32(int32 inval) public pure returns(uint32){ uint32 number = uint32(inval); number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8); number = (number >> 16) | (number << 16); return number; } function serializeInt64(int64 number) public pure returns(uint64){ uint64 v = uint64(number); v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = (v >> 32) | (v << 32); return v; } function serializeUint64(uint64 v) public pure returns(uint64){ v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = (v >> 32) | (v << 32); return v; } function serializeInt32Array(int32[] memory numbers) public pure returns(bytes memory){ bytes memory be; be = writeCompactSize((numbers.length)); for(uint i = 0;i < numbers.length; i++){ be = abi.encodePacked(be, serializeInt32(numbers[i])); } return be; } function serializeInt64Array(int64[] memory numbers) public pure returns(bytes memory){ bytes memory be; be = writeCompactSize((numbers.length)); for(uint i = 0;i < numbers.length; i++){ be = abi.encodePacked(be, serializeInt64(numbers[i])); } return be; } function serializeUint160Array(uint160[] memory numbers) public pure returns(bytes memory){ bytes memory be; be = writeCompactSize((numbers.length)); for(uint i = 0;i < numbers.length; i++){ be = abi.encodePacked(be,abi.encodePacked(numbers[i])); } return be; } function serializeCTransferDestination(VerusObjectsCommon.CTransferDestination memory ctd) public pure returns(bytes memory){ uint256 destinationSize; if ((ctd.destinationtype & VerusConstants.DEST_REGISTERCURRENCY) == VerusConstants.DEST_REGISTERCURRENCY) { destinationSize = ctd.destinationaddress.length; } else { destinationSize = ETH_ADDRESS_SIZE_BYTES; } return abi.encodePacked(ctd.destinationtype, writeCompactSize(destinationSize),ctd.destinationaddress); } function serializeCCurrencyValueMap(VerusObjects.CCurrencyValueMap memory _ccvm) public pure returns(bytes memory){ return abi.encodePacked(_ccvm.currency, serializeUint64(_ccvm.amount)); } function serializeCCurrencyValueMaps(VerusObjects.CCurrencyValueMap[] memory _ccvms) public pure returns(bytes memory){ bytes memory inProgress; inProgress = writeVarInt(uint64(_ccvms.length)); for(uint i=0; i < _ccvms.length; i++){ inProgress = abi.encodePacked(inProgress,serializeCCurrencyValueMap(_ccvms[i])); } return inProgress; } function serializeCReserveTransfer(VerusObjects.CReserveTransfer memory ct) public pure returns(bytes memory){ bytes memory output = abi.encodePacked( writeVarInt(ct.version), ct.currencyvalue.currency, writeVarInt(uint64(ct.currencyvalue.amount)), //special interpretation of a ccurrencyvalue writeVarInt(ct.flags), ct.feecurrencyid, writeVarInt(uint64(ct.fees)), serializeCTransferDestination(ct.destination), ct.destcurrencyid ); if((ct.flags & VerusConstants.RESERVE_TO_RESERVE )>0) output = abi.encodePacked(output, ct.secondreserveid); //see if it has a cross_system flag if((ct.flags & VerusConstants.CROSS_SYSTEM)>0) output = abi.encodePacked(output, ct.destsystemid); return output; } function serializeCReserveTransfers(VerusObjects.CReserveTransfer[] memory _bts, bool includeSize) public pure returns(bytes memory){ bytes memory inProgress; if (includeSize) inProgress = writeCompactSize(_bts.length); for(uint i=0; i < _bts.length; i++){ inProgress = abi.encodePacked(inProgress,serializeCReserveTransfer(_bts[i])); } return inProgress; } function serializeCCrossChainExport(VerusObjects.CCrossChainExport memory _ccce) public pure returns(bytes memory){ bytes memory part1 = abi.encodePacked( serializeUint16(_ccce.version), serializeUint16(_ccce.flags), _ccce.sourcesystemid, _ccce.hashtransfers, _ccce.destinationsystemid, _ccce.destinationcurrencyid); bytes memory part2 = abi.encodePacked( bytes2(0x0000), //Ctransferdesination is 00 type and 00 length for exporter serializeInt32(_ccce.firstinput), serializeUint32(_ccce.numinputs), writeVarInt(_ccce.sourceheightstart), writeVarInt(_ccce.sourceheightend), serializeCCurrencyValueMaps(_ccce.totalfees), serializeCCurrencyValueMaps(_ccce.totalamounts), serializeCCurrencyValueMaps(_ccce.totalburned), bytes1(0x00)); // Reservetransfers return abi.encodePacked(part1,part2); } function flipArray(bytes memory incoming) public pure returns(bytes memory){ uint256 len; len = incoming.length; bytes memory output = new bytes(len); uint256 pos = 0; while(pos < len){ output[pos] = incoming[len - pos - 1]; pos++; } return output; } function currencyParser(bytes memory input, uint256 offset) public pure returns (VerusObjects.PackedCurrencyLaunch memory returnCurrency) { uint32 nextOffset; uint32 tempUint32; address parent; address nativeCurrencyID; uint256 nftID; uint8 NativeCurrencyType; uint8 NativeCurrencyTypeNoFlags; uint32 options; nextOffset = CCC_PREFIX_TO_OPTIONS + uint32(offset); assembly { options := mload(add(input, nextOffset)) nextOffset := add(nextOffset, VERUS_ID_LENGTH) parent := mload(add(input, nextOffset)) nextOffset := add(nextOffset, 1) // one byte move forwards to read string length tempUint32 := and(mload(add(input, nextOffset)), 0x000000ff) // string length MAX 64 so will always be a byte } options = serializeUint32(options); //reverse endian returnCurrency.parent = parent; bytes memory tempname = new bytes(tempUint32); for (uint32 i = 0; i < tempUint32; i++) { tempname[i] = input[i + nextOffset]; } returnCurrency.name = string(tempname); nextOffset += tempUint32; assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) // move to read launchsystemID nextOffset := add(nextOffset, VERUS_ID_LENGTH) // move to read systemID nextOffset := add(nextOffset, CCC_NATIVE_OFFSET) // move to read the native currency type NativeCurrencyType := mload(add(input, nextOffset)) } NativeCurrencyTypeNoFlags = NativeCurrencyType & ~FLAG_MASK; //remove flags if (NativeCurrencyTypeNoFlags == VerusConstants.DEST_ETHNFT) { assembly { nextOffset := add(add(nextOffset, VERUS_ID_LENGTH), 1) //skip vector length nativeCurrencyID := mload(add(input, nextOffset)) nextOffset := add(nextOffset, CCC_TOKENID_OFFSET) nftID := mload(add(input, nextOffset)) } if (NativeCurrencyType == VerusConstants.DEST_ETHNFT) { //if there is no auxdest then it is an ERC721 returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC721_NFT_DEFINITION); } else { returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC1155_NFT_DEFINITION); } returnCurrency.flags |= uint8(VerusConstants.MAPPING_ETHEREUM_OWNED); returnCurrency.tokenID = nftID; } else if (NativeCurrencyTypeNoFlags == VerusConstants.DEST_ETH) { assembly { nextOffset := add(add(nextOffset, VERUS_ID_LENGTH), 1) //skip vector length nativeCurrencyID := mload(add(input, nextOffset)) } if (NativeCurrencyType == VerusConstants.DEST_ETH) { //if there is no auxdest then it is an ERC20 returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC20_DEFINITION); } else { returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC1155_ERC_DEFINITION); } returnCurrency.flags |= uint8(VerusConstants.MAPPING_ETHEREUM_OWNED); } else if (options & VerusConstants.OPTION_NFT_TOKEN == VerusConstants.OPTION_NFT_TOKEN) { //minted NFT from verus returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC721_NFT_DEFINITION); returnCurrency.flags |= uint8(VerusConstants.MAPPING_VERUS_OWNED); nativeCurrencyID = address(0); //The Verus NFT contract is not known by this contract so it will be set when the NFT is minted. } else { // Verus owned ERC20, ERC20 contract not known yet. returnCurrency.flags |= uint8(VerusConstants.MAPPING_ERC20_DEFINITION); returnCurrency.flags |= uint8(VerusConstants.MAPPING_VERUS_OWNED); } returnCurrency.ERCContract = nativeCurrencyID; if (NativeCurrencyType & VerusConstants.FLAG_DEST_AUX == VerusConstants.FLAG_DEST_AUX) { assembly { nextOffset := add(nextOffset, 1) //move to vector length } uint256 TokenId; TokenId = readAuxDestPK(input, nextOffset); if (NativeCurrencyTypeNoFlags == VerusConstants.DEST_ETH) { returnCurrency.tokenID = TokenId; } else if (NativeCurrencyTypeNoFlags == VerusConstants.DEST_ETHNFT && TokenId != nftID) { // if the NFT ID does not match the auxdest NFT ID then the currency is invalid returnCurrency.flags = uint8(VerusConstants.MAPPING_INVALID); return returnCurrency; } } return returnCurrency; } function deserializeTransfers(bytes memory tempSerialized, uint8 numberOfTransfers) public pure returns (VerusObjects.PackedSend[] memory tempTransfers, VerusObjects.PackedCurrencyLaunch[] memory launchTxs, uint32 counter, uint176[] memory refundAddresses) { // return value counter is a packed 32bit number first bytes is number of transfers, 3rd byte number of ETH sends 4th byte number of currencey launches tempTransfers = new VerusObjects.PackedSend[](numberOfTransfers); refundAddresses = new uint176[](numberOfTransfers); launchTxs = new VerusObjects.PackedCurrencyLaunch[](3); //max to Currency launches address tempaddress; uint64 temporaryRegister1; uint8 destinationType; uint256 nextOffset = 1; uint176 refundAddress; uint64 flags; while (nextOffset <= tempSerialized.length) { assembly { destinationType := mload(add(tempSerialized, nextOffset)) // Read one byte at the given offset if iszero(eq(and(destinationType,0xff), 1)) { revert(0, 0) // Revert the transaction if version is not equal to 1 } nextOffset := add(nextOffset, VERUS_ID_LENGTH) tempaddress := mload(add(tempSerialized, nextOffset)) // skip version 0x01 (1 byte) and read currency being sent } (temporaryRegister1, nextOffset) = readVarint(tempSerialized, nextOffset); // read varint (amount) returns next idx position (flags, nextOffset) = readVarint(tempSerialized, nextOffset); tempTransfers[uint8(counter)].currencyAndAmount = uint256(temporaryRegister1) << VerusConstants.UINT160_BITS_SIZE; //shift amount and pack tempTransfers[uint8(counter)].currencyAndAmount |= uint256(uint160(tempaddress)); nextOffset += VERUS_ID_LENGTH; //skip feecurrency id always vETH, variint already 1 byte in so 19 (temporaryRegister1, nextOffset) = readVarint(tempSerialized, nextOffset); //fees read into 'temporaryRegister1' but not used // Store Fees temporarily in the tokenID field launchTxs[2].tokenID += temporaryRegister1; assembly { nextOffset := add(nextOffset, 1) //move to read the destination type destinationType := mload(add(tempSerialized, nextOffset)) nextOffset := add(nextOffset, 1) //move to read destination vector length compactint } (temporaryRegister1, nextOffset) = readCompactSizeLE2(tempSerialized, nextOffset); // get the length of the destination // if destination an ERC Send (ETH, ERC20, ERC721, ERC1155) if (destinationType & VerusConstants.DEST_ETH == VerusConstants.DEST_ETH) { assembly { tempaddress := mload(sub(add(add(tempSerialized, nextOffset), VERUS_ID_LENGTH), 1)) //skip type +1 byte to read address } tempTransfers[uint8(counter)].destinationAndFlags = uint256(uint160(tempaddress)); } else if (destinationType & VerusConstants.DEST_REGISTERCURRENCY == VerusConstants.DEST_REGISTERCURRENCY || destinationType & VerusConstants.DEST_ETHNFT == VerusConstants.DEST_ETHNFT) { //TODO: check whether a DEST_ETHNFT is applicable here? launchTxs[(counter >> 24 & 0xff)] = currencyParser(tempSerialized, nextOffset); if (launchTxs[(counter >> 24 & 0xff)].flags != 0) { // if the currency is valid then set the iaddress to the currencyID and increment the counter. launchTxs[(counter >> 24 & 0xff)].iaddress = address(uint160(tempTransfers[uint8(counter)].currencyAndAmount)); counter += 0x1000000; //This is the Launch currency counter packed into the 4th byte } } assembly { nextOffset := add(nextOffset, temporaryRegister1) //move to vector length } if (destinationType & VerusConstants.FLAG_DEST_AUX == VerusConstants.FLAG_DEST_AUX) { (temporaryRegister1, nextOffset) = readCompactSizeLE2(tempSerialized, nextOffset); // get the length of the auxDest for (uint i = temporaryRegister1; i > 0; i--) { (temporaryRegister1, nextOffset) = readCompactSizeLE2(tempSerialized, nextOffset); // get the length of the auxDest sub array assembly { refundAddress := mload(sub(add(add(tempSerialized, nextOffset), temporaryRegister1), 1)) //skip type +1 byte to read address } refundAddresses[uint8(counter)] = refundAddress; nextOffset += temporaryRegister1; } } counter++; if(destinationType & VerusConstants.FLAG_DEST_GATEWAY == VerusConstants.FLAG_DEST_GATEWAY) { assembly { nextOffset := add(nextOffset, TRANSFER_GATEWAYSKIP) //move to vector length } } assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to vector length } if(flags & VerusConstants.RESERVE_TO_RESERVE == VerusConstants.RESERVE_TO_RESERVE) { assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to vector length } } if(flags & VerusConstants.CROSS_SYSTEM == VerusConstants.CROSS_SYSTEM ) { assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to vector length } } } return (tempTransfers, launchTxs, counter, refundAddresses); } function readVarint(bytes memory buf, uint idx) public pure returns (uint64 v, uint retidx) { uint8 b; assembly { let end := add(idx, 10) let i := idx retidx := add(idx, 1) for {} lt(i, end) {} { b := mload(add(buf, retidx)) i := add(i, 1) v := or(shl(7, v), and(b, 0x7f)) if iszero(eq(and(b, 0x80), 0x80)) { break } v := add(v, 1) retidx := add(retidx, 1) } } } function readCompactSizeLE2(bytes memory incoming, uint256 offset) public pure returns(uint64 v, uint retidx) { uint8 oneByte; assembly { oneByte := mload(add(incoming, offset)) } offset++; if (oneByte < 253) { return (uint64(oneByte), offset); } else if (oneByte == 253) { offset++; uint16 twoByte; assembly { twoByte := mload(add(incoming, offset)) } return (((twoByte << 8) & 0xffff) | twoByte >> 8, offset + 1); } return (0, offset); } function deserializeTransfer(bytes memory serialized) public view returns (VerusObjects.CReserveTransfer memory transfer){ uint256 nextOffset; address tempaddress; uint64 tempReg1; uint8 tempuint8; assembly { nextOffset := add(nextOffset, 1) //move to read the version type tempuint8 := mload(add(serialized, nextOffset)) // read the version type nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to read the currencyID tempaddress := mload(add(serialized, nextOffset)) // read the version currencyID } transfer.version = tempuint8; transfer.currencyvalue.currency = tempaddress; (tempReg1, nextOffset) = readVarint(serialized, nextOffset); // read varint (nValue) returns next idx position transfer.currencyvalue.amount = tempReg1; //copy the value (tempReg1, nextOffset) = readVarint(serialized, nextOffset); //read the flags transfer.flags = uint32(tempReg1); //copy the flags assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to read the destination type, note already 1 byte in so only move 19 tempaddress := mload(add(serialized, nextOffset)) // read the feecurrencyid } transfer.feecurrencyid = tempaddress; //copy the feecurrency (transfer.fees, nextOffset) = readVarint(serialized, nextOffset); //read the fees and copy into structure assembly { nextOffset := add(nextOffset, 1) tempuint8 := mload(add(serialized, nextOffset)) // already at destination type location so read byte } transfer.destination.destinationtype = tempuint8; //copy destination type if (tempuint8 == (VerusConstants.DEST_ETH + VerusConstants.FLAG_DEST_GATEWAY + VerusConstants.FLAG_DEST_AUX) || tempuint8 == VerusConstants.DEST_ID || tempuint8 == VerusConstants.DEST_PKH) { assembly { nextOffset := add(nextOffset, 1) // skip vector length nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to read the destinationaddress, note already at vector length. tempaddress := mload(add(serialized, nextOffset)) // read desitination address note always 20 bytes. } } else {revert();} // note only 3 types allowed at the moment bytes memory tempBouncebacktype; if (tempuint8 == (VerusConstants.DEST_ETH + VerusConstants.FLAG_DEST_GATEWAY + VerusConstants.FLAG_DEST_AUX)) { tempBouncebacktype = this.slice(serialized, nextOffset, nextOffset + ETH_SEND_GATEWAY_AND_AUX_DEST); assembly { nextOffset := add(nextOffset, ETH_SEND_GATEWAY_AND_AUX_DEST) // skip vector length } } transfer.destination.destinationaddress = abi.encodePacked(tempaddress, tempBouncebacktype); assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to read the destinationcurrency address tempaddress := mload(add(serialized, nextOffset)) // read the destinationcurrency address } transfer.destcurrencyid = tempaddress; if (transfer.flags & VerusConstants.RESERVE_TO_RESERVE == VerusConstants.RESERVE_TO_RESERVE) { assembly { nextOffset := add(nextOffset, VERUS_ID_LENGTH) //move to read the secondreserveid tempaddress := mload(add(serialized, nextOffset)) // read the secondreserveid } transfer.secondreserveid = tempaddress; } return transfer; } function slice (bytes calldata data, uint256 start, uint256 end) public pure returns (bytes memory) { return data[start:end]; } function _toLower(bytes memory bStr) internal pure returns (string memory) { bytes memory bLower = new bytes(bStr.length); for (uint i = 0; i < bStr.length; i++) { // Uppercase character... if ((uint8(bStr[i]) >= 65) && (uint8(bStr[i]) <= 90)) { // So we add 32 to make it lowercase bLower[i] = bytes1(uint8(bStr[i]) + 32); } else { bLower[i] = bStr[i]; } } return string(bLower); } function sha256d(string memory _string) internal pure returns(bytes32){ return sha256(abi.encodePacked(sha256(abi.encodePacked(_string)))); } function checkIAddress(VerusObjects.PackedCurrencyLaunch memory _tx) public pure{ address calculated; calculated = address(ripemd160(abi.encodePacked(sha256(abi.encodePacked(sha256d(string(abi.encodePacked(_tx.parent,sha256d(_toLower(bytes(_tx.name))))))))))); require(calculated == _tx.iaddress, "Iaddress does not match"); } function readAuxDestPK(bytes memory input, uint256 nextOffset) internal pure returns(uint256) { uint64 temporaryRegister1; uint256 tokenID; (temporaryRegister1, nextOffset) = readCompactSizeLE2(input, nextOffset); // get the length of the auxDest if (temporaryRegister1 == 1) { (temporaryRegister1, nextOffset) = readCompactSizeLE2(input, nextOffset); // get the length of the auxDest sub array, this will be a CReserveDestination if (uint8(input[nextOffset - 1]) == VerusConstants.DEST_PK && uint8(input[nextOffset]) == 0x21 //check for PKH and 33 bytes && uint8(input[nextOffset + 1]) == VerusConstants.DEST_PKH) { assembly { nextOffset := add(nextOffset, 1) //move to vector length nextOffset := add(nextOffset, 1) //move to PKtype nextOffset := add(nextOffset, CCC_TOKENID_OFFSET) //move to TokenId address tokenID := mload(add(input, nextOffset)) } return tokenID; } } return tokenID; } } ================================================ FILE: contracts/VerusBridge/dsrinterface.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; interface VatLike { function hope(address) external; } interface PotLike { function vat() external view returns (address); function chi() external view returns (uint256); function rho() external view returns (uint256); function drip() external returns (uint256); function join(uint256) external; function exit(uint256) external; } interface JoinLike { function dai() external view returns (address); function join(address, uint256) external; function exit(address, uint256) external; } ================================================ FILE: contracts/VerusNotarizer/NotarizationSerializer.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusObjects.sol"; import "../Libraries/VerusObjectsCommon.sol"; import "../VerusBridge/UpgradeManager.sol"; import "../Storage/StorageMaster.sol"; contract NotarizationSerializer is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; address immutable DAI; address immutable MKR; constructor(address vETH, address Bridge, address Verus, address Dai, address Mkr){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; DAI = Dai; MKR = Mkr; } uint8 constant CURRENCY_LENGTH = 20; uint8 constant BYTES32_LENGTH = 32; uint8 constant TWO2BYTES32_LENGTH = 64; uint8 constant FLAG_FRACTIONAL = 1; uint8 constant FLAG_REFUNDING = 4; uint8 constant FLAG_LAUNCHCONFIRMED = 0x10; uint8 constant FLAG_LAUNCHCOMPLETEMARKER = 0x20; uint8 constant AUX_DEST_ETH_VEC_LENGTH = 22; uint8 constant AUX_DEST_VOTE_HASH = 21; uint8 constant VOTE_BYTE_POSITION = 22; uint8 constant REQUIREDAMOUNTOFVOTES = 100; uint8 constant WINNINGAMOUNT = 51; uint8 constant UINT64_BYTES_SIZE = 8; uint8 constant PROOF_TYPE_ETH = 2; enum Currency {VETH, DAI, VERUS, MKR} function initialize() external { rollingVoteIndex = VerusConstants.DEFAULT_INDEX_VALUE; } function readVarint(bytes memory buf, uint32 idx) public pure returns (uint32 v, uint32 retidx) { uint8 b; // store current byte content for (uint32 i=0; i<10; i++) { b = uint8(buf[i+idx]); v = (v << 7) | b & 0x7F; if (b & 0x80 == 0x80) v++; else return (v, idx + i + 1); } revert(); // i=10, invalid varint stream } function deserializeNotarization(bytes memory notarization) external returns (bytes32 proposerAndLaunched, bytes32 prevnotarizationtxid, bytes32 hashprevcrossnotarization, bytes32 stateRoot, uint32 height) { uint32 nextOffset; uint16 bridgeConverterLaunched; uint8 proposerType; uint32 notarizationFlags; uint176 proposerMain; VerusObjectsCommon.UintReader memory readerLen; readerLen = readVarintStruct(notarization, 0); // get the length of the varint version readerLen = readVarintStruct(notarization, readerLen.offset); // get the length of the flags notarizationFlags = uint32(readerLen.value); nextOffset = readerLen.offset; assembly { nextOffset := add(nextOffset, 1) // move to read type proposerType := mload(add(nextOffset, notarization)) if gt(and(proposerType, 0xff), 0) { nextOffset := add(nextOffset, 21) // move to proposer, type and vector length proposerMain := mload(add(nextOffset, notarization)) //remove type flags of proposer. proposerMain := and(proposerMain, 0x0fffffffffffffffffffffffffffffffffffffffffff) } } if (proposerType & VerusConstants.FLAG_DEST_AUX == VerusConstants.FLAG_DEST_AUX) { nextOffset += 1; // goto auxdest parent vec length position nextOffset = processAux(notarization, nextOffset); nextOffset -= 1; // NOTE: Next Varint call takes array pos not array pos +1 } //position 0 of the rolling vote is use to determine whether votes have started else if (rollingVoteIndex != VerusConstants.DEFAULT_INDEX_VALUE){ castVote(address(0)); } assembly { nextOffset := add(nextOffset, CURRENCY_LENGTH) //skip currencyid } (, nextOffset) = deserializeCoinbaseCurrencyState(notarization, nextOffset); assembly { nextOffset := add(nextOffset, 4) // skip notarizationheight nextOffset := add(nextOffset, BYTES32_LENGTH) // move to read prevnotarizationtxid prevnotarizationtxid := mload(add(notarization, nextOffset)) // prevnotarizationtxid nextOffset := add(nextOffset, 4) //skip prevnotarizationout nextOffset := add(nextOffset, BYTES32_LENGTH) // move to read hashprevcrossnotarization hashprevcrossnotarization := mload(add(notarization, nextOffset)) // hashprevcrossnotarization nextOffset := add(nextOffset, 4) //skip prevheight nextOffset := add(nextOffset, 1) //move to read length of notarizationcurrencystate } readerLen = readCompactSizeLE(notarization, nextOffset); // get the length of the currencyState nextOffset = readerLen.offset - 1; //readCompactSizeLE returns offset of next byte so move back to start of currencyState for (uint i = 0; i < readerLen.value; i++) { uint16 temp; // store the currency state flags (temp, nextOffset) = deserializeCoinbaseCurrencyState(notarization, nextOffset); bridgeConverterLaunched |= temp; } proposerAndLaunched = bytes32(uint256(proposerMain)); if (!bridgeConverterActive && bridgeConverterLaunched > 0) { proposerAndLaunched |= bytes32(uint256(bridgeConverterLaunched) << VerusConstants.UINT176_BITS_SIZE); // Shift 16bit value 22 bytes to pack in bytes32 } nextOffset++; //move forwards to read le readerLen = readCompactSizeLE(notarization, nextOffset); // get the length of proofroot array (stateRoot, height) = deserializeProofRoots(notarization, uint32(readerLen.value), nextOffset); } function deserializeCoinbaseCurrencyState(bytes memory notarization, uint32 nextOffset) private returns (uint16, uint32) { address currencyid; uint16 bridgeLaunched; uint16 flags; assembly { nextOffset := add(nextOffset, 2) // move to version nextOffset := add(nextOffset, 2) // move to flags flags := mload(add(notarization, nextOffset)) // flags nextOffset := add(nextOffset, CURRENCY_LENGTH) //skip notarization currencystatecurrencyid currencyid := mload(add(notarization, nextOffset)) // currencyid } flags = (flags >> 8) | (flags << 8); if ((currencyid == BRIDGE) && flags & (FLAG_FRACTIONAL + FLAG_REFUNDING + FLAG_LAUNCHCONFIRMED + FLAG_LAUNCHCOMPLETEMARKER) == (FLAG_FRACTIONAL + FLAG_LAUNCHCONFIRMED + FLAG_LAUNCHCOMPLETEMARKER)) { bridgeLaunched = 1; } assembly { nextOffset := add(nextOffset, 1) // move to read currency state length } VerusObjectsCommon.UintReader memory readerLen; readerLen = readCompactSizeLE(notarization, nextOffset); // get the length currencies // reserves[2] contain the scaled reserve amounts for ETH and DAI uint daiEthVRSCMKRReserves; if (currencyid == BRIDGE && bridgeConverterActive) { daiEthVRSCMKRReserves = getReserves(notarization, nextOffset, uint8(readerLen.value)); claimableFees[bytes32(uint256(uint160(VerusConstants.VDXF_ETH_DAI_VRSC_LAST_RESERVES)))] = daiEthVRSCMKRReserves; //store the fees in the notaryFeePool } nextOffset = nextOffset + (uint32(readerLen.value) * BYTES32_LENGTH) + 2; readerLen = readVarintStruct(notarization, nextOffset); // get the length of the initialsupply readerLen = readVarintStruct(notarization, readerLen.offset); // get the length of the emitted readerLen = readVarintStruct(notarization, readerLen.offset); // get the length of the supply nextOffset = readerLen.offset; assembly { nextOffset := add(nextOffset, 33) //skip coinbasecurrencystate first 4 items fixed at 4 x 8 } readerLen = readCompactSizeLE(notarization, nextOffset); // get the length of the reservein array of uint64 nextOffset = readerLen.offset + (uint32(readerLen.value) * 60) + 6; //skip 60 bytes of rest of state knowing array size always same as first return (bridgeLaunched, nextOffset); } function getReserves (bytes memory notarization, uint32 nextOffset, uint8 currenciesLen) private view returns (uint) { Currency[4] memory currencyIndexes; for (uint8 i = 0; i < currenciesLen; i++) { address currency; assembly { nextOffset := add(nextOffset, CURRENCY_LENGTH) // move to read currency length currency := mload(add(notarization, nextOffset)) // move to read currencyid } if (currency == VETH) { currencyIndexes[i] = Currency.VETH; } else if (currency == DAI) { currencyIndexes[i] = Currency.DAI; } else if (currency == VERUS) { currencyIndexes[i] = Currency.VERUS; } else if (currency == MKR) { currencyIndexes[i] = Currency.MKR; } } //Skip the weights nextOffset = nextOffset + 1 + (uint32(currenciesLen) * 4) + 1; //move to read len of reserves //read the reserves, position [0..63] for DAI, [64..127] for ETH [128..191] for VRSC pack into uint 64bit chunks uint256 ethToDaiRatios; for (uint8 i = 0; i < currenciesLen; i++) { uint64 reserve; assembly { nextOffset := add(nextOffset, UINT64_BYTES_SIZE) // move to read currency length reserve := mload(add(notarization, nextOffset)) // move to read currencyid } ethToDaiRatios |= uint256(serializeUint64(reserve)) << (uint256(currencyIndexes[i]) << 6); } assembly { nextOffset := add(nextOffset, 1) // move forward to read next varint. } return ethToDaiRatios; } function deserializeProofRoots (bytes memory notarization, uint32 size, uint32 nextOffset) private view returns (bytes32 stateRoot, uint32 height) { for (uint i = 0; i < size; i++) { uint16 proofType; address systemID; bytes32 tempStateRoot; uint32 tempHeight; assembly { nextOffset := add(nextOffset, 2) // move to version nextOffset := add(nextOffset, 2) // move to read type proofType := mload(add(notarization, nextOffset)) // read proofType nextOffset := add(nextOffset, CURRENCY_LENGTH) // move to read systemID systemID := mload(add(notarization, nextOffset)) nextOffset := add(nextOffset, 4) // move to height tempHeight := mload(add(notarization, nextOffset)) nextOffset := add(nextOffset, BYTES32_LENGTH) // move to read stateroot tempStateRoot := mload(add(notarization, nextOffset)) nextOffset := add(nextOffset, BYTES32_LENGTH) // move to read blockhash nextOffset := add(nextOffset, BYTES32_LENGTH) // move to power } if(systemID == VERUS) { stateRoot = tempStateRoot; height = serializeUint32(tempHeight); //swapendian } //swap 16bit endian if(((proofType >> 8) | (proofType << 8)) == PROOF_TYPE_ETH){ assembly { nextOffset := add(nextOffset, 8) // move to gasprice } } } } function readVarintStruct(bytes memory buf, uint idx) public pure returns (VerusObjectsCommon.UintReader memory) { uint8 b; uint64 v; uint retidx; assembly { ///assemmbly 2267 GAS let end := add(idx, 10) let i := idx retidx := add(idx, 1) for {} lt(i, end) {} { b := mload(add(buf, retidx)) i := add(i, 1) v := or(shl(7, v), and(b, 0x7f)) if iszero(eq(and(b, 0x80), 0x80)) { break } v := add(v, 1) retidx := add(retidx, 1) } } return VerusObjectsCommon.UintReader(uint32(retidx), v); } function processAux (bytes memory firstObj, uint32 nextOffset) private returns (uint32) { VerusObjectsCommon.UintReader memory readerLen; readerLen = readCompactSizeLE(firstObj, nextOffset); // get the length of the auxDest nextOffset = readerLen.offset; uint arraySize = readerLen.value; address tempAddress; for (uint i = 0; i < arraySize; i++) { readerLen = readCompactSizeLE(firstObj, nextOffset); // get the length of the auxDest sub array assembly { tempAddress := mload(add(add(firstObj, nextOffset),AUX_DEST_ETH_VEC_LENGTH)) } castVote(tempAddress); nextOffset = (readerLen.offset + uint32(readerLen.value)); } return nextOffset; } function castVote(address votetxid) private { // If the vote is address(0) and the vote has not started, or the vote hash has already been used, return if ((votetxid == address(0) && rollingVoteIndex == VerusConstants.DEFAULT_INDEX_VALUE) || successfulVoteHashes[votetxid] == VerusConstants.MAX_UINT256) { return; } // if the vote hash has not been used, save the vote start timestamp if(votetxid != address(0) && successfulVoteHashes[votetxid] == 0) { successfulVoteHashes[votetxid] = block.timestamp; } else if (votetxid != address(0) && (block.timestamp - successfulVoteHashes[votetxid]) > (VerusConstants.SECONDS_IN_DAY * 20)) { // if 20 days have passed since the vote started, set the vote hash as used successfulVoteHashes[votetxid] = VerusConstants.MAX_UINT256; // reset the rolling vote index to DEFAULT, if there are other hashes they will continue, otherwise voting will go idle. rollingVoteIndex = VerusConstants.DEFAULT_INDEX_VALUE; return; } if(rollingVoteIndex > (VerusConstants.VOTE_LENGTH - 1)) { rollingVoteIndex = 0; } else { rollingVoteIndex = rollingVoteIndex + 1; } // save an empty global write if the value to be written is the same as the current value if (votetxid != rollingUpgradeVotes[rollingVoteIndex]) { rollingUpgradeVotes[rollingVoteIndex] = votetxid; } } function readCompactSizeLE(bytes memory incoming, uint32 offset) public pure returns(VerusObjectsCommon.UintReader memory) { uint8 oneByte; assembly { oneByte := mload(add(incoming, offset)) } offset++; if (oneByte < 253) { return VerusObjectsCommon.UintReader(offset, oneByte); } else if (oneByte == 253) { offset++; uint16 twoByte; assembly { twoByte := mload(add(incoming, offset)) } return VerusObjectsCommon.UintReader(offset + 1, ((twoByte << 8) & 0xffff) | twoByte >> 8); } return VerusObjectsCommon.UintReader(offset, 0); } function serializeUint32(uint32 number) public pure returns(uint32){ // swap bytes number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8); number = (number >> 16) | (number << 16); return number; } function serializeUint64(uint64 v) public pure returns(uint64){ v = ((v & 0xFF00FF00FF00FF00) >> 8) | ((v & 0x00FF00FF00FF00FF) << 8); // swap 2-byte long pairs v = ((v & 0xFFFF0000FFFF0000) >> 16) | ((v & 0x0000FFFF0000FFFF) << 16); // swap 4-byte long pairs v = (v >> 32) | (v << 32); return v; } } ================================================ FILE: contracts/VerusNotarizer/NotaryTools.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusConstants.sol"; import "../Libraries/VerusObjectsNotarization.sol"; import "../VerusBridge/VerusSerializer.sol"; import "./NotarizationSerializer.sol"; import "../MMR/VerusBlake2b.sol"; import "../VerusBridge/UpgradeManager.sol"; import "../Storage/StorageMaster.sol"; import "../VerusBridge/Token.sol"; import "../VerusBridge/TokenManager.sol"; contract NotaryTools is VerusStorage { uint8 constant TYPE_REVOKE = 2; uint8 constant TYPE_RECOVER = 3; uint8 constant TYPE_AUTO_REVOKE = 4; uint8 constant NUM_ADDRESSES_FOR_REVOKE = 2; uint8 constant COMPLETE = 2; uint8 constant ERROR = 4; using VerusBlake2b for bytes; function initialize() external { // 0xf7893a5c9175baa68c3d88eb145012ca40c90c59 == 20bytes hex of vwBTC.vETH@ (iS3NjE3XRYWoHRoovpLhFnbDraCq7NFStf) uint256 currencyAmountInContract = verusToERC20mapping[0xF7893a5c9175bAa68C3d88EB145012CA40C90C59].tokenIndex; // 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 == ERC20 Contract address of Wrapped BTC (WBTC) uint256 actualBalance = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599).balanceOf(address(this)); uint256 amountToSend = actualBalance - currencyAmountInContract - 5809652; // Send 0.05809652 WBTC (8 decimals) to owed address IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599).transfer(0x16770EafcdBEFf2AE73ccD680694f53a8D40df55, 5809652); if (amountToSend > 0) { // if there is any excess, send it to a refund address; IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599).transfer(0x8727eE29C1C88b5b2a0Fed4721F92Cc9cd44583b, amountToSend); } } function updateNotarizer(address notarizer, address mainAddress, address revokeAddress, uint8 state) private { notaryAddressMapping[notarizer] = VerusObjects.notarizer(mainAddress, revokeAddress, state); } function recoverString(bytes memory be, uint8 vs, bytes32 rs, bytes32 ss) public pure returns (address) { bytes32 hashValue; hashValue = sha256(abi.encodePacked(writeCompactSize(be.length),be)); hashValue = sha256(abi.encodePacked(uint8(19),hex"5665727573207369676e656420646174613a0a", hashValue)); //TODO: move to constants prefix = 19(len) + "Verus signed data:\n" return ecrecover(hashValue, vs - 4, rs, ss); } function revokeWithMainAddress(bytes calldata) public returns (bool) { for (uint i; i= ((notaries.length >> 1) + 1), "not enough signatures"); notaryAddressMapping[notarizerBeingRevoked].state = VerusConstants.NOTARY_REVOKED; return true; } function recoverWithRecoveryAddress(bytes calldata dataIn) public returns (uint8) { VerusObjects.upgradeInfo memory _newRecoveryInfo = abi.decode(dataIn, (VerusObjects.upgradeInfo)); bytes memory be; require(saltsUsed[_newRecoveryInfo.salt] == false, "salt Already used"); saltsUsed[_newRecoveryInfo.salt] = true; require(_newRecoveryInfo.contracts.length == NUM_ADDRESSES_FOR_REVOKE, "Input Identities wrong length"); require(_newRecoveryInfo.upgradeType == TYPE_RECOVER, "Wrong type of package"); require(notaryAddressMapping[_newRecoveryInfo.notarizerID].state == VerusConstants.NOTARY_REVOKED, "Notary not revoked"); be = bytesToString(abi.encodePacked(_newRecoveryInfo.contracts[0],_newRecoveryInfo.contracts[1], uint8(_newRecoveryInfo.upgradeType), _newRecoveryInfo.salt)); address signer = recoverString(be, _newRecoveryInfo._vs, _newRecoveryInfo._rs, _newRecoveryInfo._ss); if (signer != notaryAddressMapping[_newRecoveryInfo.notarizerID].recovery) { revert(); } updateNotarizer(_newRecoveryInfo.notarizerID, _newRecoveryInfo.contracts[0], _newRecoveryInfo.contracts[1], VerusConstants.NOTARY_VALID); return COMPLETE; } function recoverWithMultiSig(bytes calldata dataIn) public returns (uint8) { // Recover with a quorum of notary sigs. (VerusObjects.revokeRecoverInfo[] memory _recoverPacket, address notarizerBeingRecovered, address newMainAddr, address newRevokeAddr) = abi.decode(dataIn, (VerusObjects.revokeRecoverInfo[], address, address, address)); bytes memory be; require(notaryAddressMapping[notarizerBeingRecovered].state == VerusConstants.NOTARY_REVOKED, "Notary not revoked"); uint counter; for (uint i = 0; i < _recoverPacket.length; i++) { for (uint j = i + 1; j < _recoverPacket.length; j++) { if (_recoverPacket[i].notarizerID == _recoverPacket[j].notarizerID) { revert("Duplicate signatures"); } } require(saltsUsed[_recoverPacket[i].salt] == false, "salt Already used"); saltsUsed[_recoverPacket[i].salt] = true; be = bytesToString(abi.encodePacked(uint8(TYPE_RECOVER),notarizerBeingRecovered, newMainAddr, newRevokeAddr, _recoverPacket[i].salt)); address signer = recoverString(be, _recoverPacket[i]._vs, _recoverPacket[i]._rs, _recoverPacket[i]._ss); if (signer == notaryAddressMapping[_recoverPacket[i].notarizerID].recovery && notaryAddressMapping[_recoverPacket[i].notarizerID].state == VerusConstants.NOTARY_VALID) { counter++; } } require(counter >= ((notaries.length >> 1) + 1), "not enough sigs"); updateNotarizer(notarizerBeingRecovered, newMainAddr, newRevokeAddr, VerusConstants.NOTARY_VALID); return COMPLETE; } function bytesToString (bytes memory input) private pure returns (bytes memory output) { bytes memory _string = new bytes(input.length * 2); bytes memory HEX = "0123456789abcdef"; for(uint i = 0; i < input.length; i++) { _string[i*2] = HEX[uint8(input[i] >> 4)]; _string[1+i*2] = HEX[uint8(input[i] & 0x0f)]; } return _string; } function writeCompactSize(uint newNumber) internal pure returns(bytes memory) { bytes memory output; if (newNumber < uint8(253)) { output = abi.encodePacked(uint8(newNumber)); } else if (newNumber <= 0xFFFF) { output = abi.encodePacked(uint8(253),uint8(newNumber & 0xff),uint8(newNumber >> 8)); } else if (newNumber <= 0xFFFFFFFF) { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24)); } else { output = abi.encodePacked(uint8(254),uint8(newNumber & 0xff),uint8(newNumber >> 8),uint8(newNumber >> 16),uint8(newNumber >> 24),uint8(newNumber >> 32),uint8(newNumber >> 40),uint8(newNumber >> 48),uint8(newNumber >> 56)); } return output; } function launchContractTokens(bytes calldata data) external { VerusObjects.setupToken[] memory tokensToDeploy = abi.decode(data, (VerusObjects.setupToken[])); for (uint256 i = 0; i < tokensToDeploy.length; i++) { (bool success,) = contracts[uint160(VerusConstants.ContractType.TokenManager)].delegatecall(abi.encodeWithSelector(TokenManager.recordToken.selector, tokensToDeploy[i].iaddress, tokensToDeploy[i].erc20ContractAddress, tokensToDeploy[i].name, tokensToDeploy[i].ticker, tokensToDeploy[i].flags, uint256(0) )); require(success); } } } ================================================ FILE: contracts/VerusNotarizer/VerusNotarizer.sol ================================================ // SPDX-License-Identifier: MIT // Bridge between ethereum and verus pragma solidity >=0.8.9; pragma abicoder v2; import "../Libraries/VerusConstants.sol"; import "../Libraries/VerusObjectsNotarization.sol"; import "./NotarizationSerializer.sol"; import "../MMR/VerusBlake2b.sol"; import "../VerusBridge/CreateExports.sol"; import "../VerusBridge/UpgradeManager.sol"; import "../Storage/StorageMaster.sol"; import "../VerusBridge/SubmitImports.sol"; contract VerusNotarizer is VerusStorage { address immutable VETH; address immutable BRIDGE; address immutable VERUS; constructor(address vETH, address Bridge, address Verus){ VETH = vETH; BRIDGE = Bridge; VERUS = Verus; } uint8 constant FORKS_DATA_OFFSET_FOR_HEIGHT = 224; uint32 constant FORKS_DATA_CONFIRMED_PROPOSER = 96; uint32 constant PROOF_HEIGHT_LOCATION = 68 - 32; //NOTE: removed blockhash from proofroot, so this is now 68 bytes minus 32 bytes for the blockhash uint32 constant LENGTH_OF_FORK_DATA = 96; // notarization vdxf key bytes22 constant vdxfcodePlusVersion = bytes22(0x01367Eaadd291E1976ABc446A143f83c2D4D2C5a8401); event NewNotarization (bytes32); using VerusBlake2b for bytes; function currentNotariesLength() public view returns(uint8){ return uint8(notaries.length); } function setLatestData(bytes calldata serializedNotarization, bytes32 txid, uint32 n, bytes calldata data ) external { require(!knownNotarizationTxids[txid], "known TXID"); knownNotarizationTxids[txid] = true; (uint8[] memory _vs, bytes32[] memory _rs, bytes32[] memory _ss, uint32[] memory blockheights, address[] memory notaryAddresses) = abi.decode(data,(uint8[],bytes32[], bytes32[],uint32[],address[])); bytes32 keccakNotarizationHash; bytes32 txidHash; txidHash = keccak256(abi.encodePacked(txid, serializeUint32(n))); keccakNotarizationHash = keccak256(serializedNotarization); uint i; bytes memory tempBytes; tempBytes = abi.encodePacked( vdxfcodePlusVersion, txidHash, VERUS, serializeUint32(blockheights[i]), notaryAddresses[i], keccakNotarizationHash); for(; i < notaryAddresses.length; i++) { if (i < (notaryAddresses.length - 1)) { checkunique(notaryAddresses, i); } bytes32 hashedNotarizationByID; // hash the notarizations with the vdxf key, system, height & NotaryID. the address is masked in 98 bytes in . assembly { mstore(add(tempBytes, 98), or(and(mload(add(tempBytes, 98)), shl(160, 0xffffffffffffffffffffffff)), mload(add(notaryAddresses, mul(add(i,1), 32))))) hashedNotarizationByID := keccak256(add(tempBytes, 0x20), mload(tempBytes)) } // hashedNotarizationByID = keccak256(tempBytes); if (ecrecover(hashedNotarizationByID, _vs[i]-4, _rs[i], _ss[i]) != notaryAddressMapping[notaryAddresses[i]].main) { revert("Invalid notary signature"); } if (notaryAddressMapping[notaryAddresses[i]].state != VerusConstants.NOTARY_VALID) { revert("Notary revoked"); } } if(i < ((notaries.length >> 1) + 1 )) { revert("not enough notary signatures"); } checkNotarization(serializedNotarization, txid, uint64(n)); } function checkNotarization(bytes calldata serializedNotarization, bytes32 txid, uint64 voutAndHeight ) private { bytes32 blakeNotarizationHash; blakeNotarizationHash = serializedNotarization.createHash(); address notarizationSerializerAddress = contracts[uint(VerusConstants.ContractType.NotarizationSerializer)]; (bool success, bytes memory returnBytes) = notarizationSerializerAddress.delegatecall(abi.encodeWithSignature("deserializeNotarization(bytes)", serializedNotarization)); require(success); (bytes32 launchedAndProposer, bytes32 prevnotarizationtxid, bytes32 hashprevnotarization, bytes32 stateRoot, uint32 verusProofheight) = abi.decode(returnBytes, (bytes32, bytes32, bytes32, bytes32, uint32)); voutAndHeight |= uint64(verusProofheight) << 32; // pack two 32bit numbers into one uint64 launchedAndProposer |= bytes32(uint256(voutAndHeight) << VerusConstants.NOTARIZATION_VOUT_NUM_INDEX); // Also pack in the voutnum at the end of the uint256 setNotarizationProofRoot(blakeNotarizationHash, hashprevnotarization, txid, prevnotarizationtxid, launchedAndProposer, stateRoot); // If the bridge is active and VRSC remaining has not been sent if (remainingLaunchFeeReserves != 0 && bridgeConverterActive) { if (remainingLaunchFeeReserves > (VerusConstants.verusTransactionFee * 2)) { bool success2; bytes memory retData; (success2, retData) = contracts[uint(VerusConstants.ContractType.SubmitImports)].delegatecall(abi.encodeWithSelector(SubmitImports.sendBurnBackToVerus.selector, 0, VERUS, 0)); require(success2); (VerusObjects.CReserveTransfer memory LPtransfer,) = abi.decode(retData, (VerusObjects.CReserveTransfer, bool)); (success2, retData) = contracts[uint(VerusConstants.ContractType.CreateExport)].delegatecall(abi.encodeWithSelector(CreateExports.externalCreateExportCall.selector, abi.encode(LPtransfer, true))); require(success2); remainingLaunchFeeReserves = 0; } } emit NewNotarization(blakeNotarizationHash); } function decodeNotarization(uint index) public view returns (VerusObjectsNotarization.NotarizationForks[] memory) { uint32 nextOffset; bytes storage tempArray = bestForks[index]; bytes32 hashOfNotarization; bytes32 txid; bytes32 packedPositions; bytes32 slotHash; VerusObjectsNotarization.NotarizationForks[] memory retval = new VerusObjectsNotarization.NotarizationForks[]((tempArray.length / LENGTH_OF_FORK_DATA) + 1); if (tempArray.length > 1) { bytes32 slot; assembly { mstore(add(slot, 32),tempArray.slot) slotHash := keccak256(add(slot, 32), 32) } for (int i = 0; i < int(tempArray.length / LENGTH_OF_FORK_DATA); i++) { assembly { hashOfNotarization := sload(add(slotHash,nextOffset)) nextOffset := add(nextOffset, 1) txid := sload(add(slotHash,nextOffset)) nextOffset := add(nextOffset, 1) packedPositions :=sload(add(slotHash,nextOffset)) nextOffset := add(nextOffset, 1) } retval[uint(i)] = VerusObjectsNotarization.NotarizationForks(hashOfNotarization, txid, packedPositions); } } return retval; } function encodeNotarization(uint index, VerusObjectsNotarization.NotarizationForks memory notarizations)private { if (bestForks.length < index + 1) { bestForks.push(""); //initialize empty bytes array } bestForks[index] = abi.encodePacked(bestForks[index], notarizations.hashOfNotarization, notarizations.txid, notarizations.proposerPacked); } function encodeStandardNotarization(VerusObjectsNotarization.NotarizationForks memory firstNotarization, bytes memory secondNotarization)private { bestForks[0] = abi.encodePacked(firstNotarization.hashOfNotarization, firstNotarization.txid, firstNotarization.proposerPacked, secondNotarization); } function setNotarizationProofRoot(bytes32 hashedNotarization, bytes32 hashprevnotarization, bytes32 txidHash, bytes32 hashprevtxid, bytes32 proposer, bytes32 stateRoot) private { int forkIdx = -1; int forkPos; VerusObjectsNotarization.NotarizationForks[] memory notarizations; for (int i = 0; i < int(bestForks.length) ; i++) { notarizations = decodeNotarization(uint(i)); // Notarization length is always +1 more than the amount we have, as slot is ready to be filled. for (int j = int(notarizations.length) - 2; j >= 0; j--) { if (hashprevnotarization == notarizations[uint(j)].hashOfNotarization || hashprevtxid == notarizations[uint(j)].txid) { forkIdx = i; forkPos = j; break; } } if (forkIdx > -1) { break; } } if (forkIdx == -1 && bestForks.length != 0) { revert("invalid notarization hash"); } if (forkIdx == -1){ forkIdx = 0; } if (forkPos != int(notarizations.length) - 2 && bestForks.length != 0) { forkIdx = int(bestForks.length); encodeNotarization(uint(forkIdx), notarizations[uint(0)]); } // If the position that is matched is the second stored one, then that becomes the new confirmed. if(forkPos == 1) { if (bestForks.length > 1) { delete bestForks; bestForks.push(""); } //pack vout in at the end of the proposer 22 bytes ctransferdest encodeStandardNotarization(notarizations[1], abi.encode(hashedNotarization, txidHash, proposer)); // The proofs are indexed by height, the height is stored in the proposers last 32 bits. Stored ine the proof is stateroot and the previous proof height. proofs[bytes32(uint256(uint32(uint256(proposer >> FORKS_DATA_OFFSET_FOR_HEIGHT))))] = abi.encodePacked(stateRoot, uint32(uint256(notarizations[1].proposerPacked) >> FORKS_DATA_OFFSET_FOR_HEIGHT)); // Set bridge launched if confirmed notarization contains Bridge Launched bit packed on the end of proposer if (!bridgeConverterActive && ((uint256(notarizations[1].proposerPacked) >> VerusConstants.UINT176_BITS_SIZE) & 0xff) == 1) { bridgeConverterActive = true; } } else { encodeNotarization(uint(forkIdx), VerusObjectsNotarization.NotarizationForks(hashedNotarization, txidHash, proposer)); } } function checkunique(address[] memory ids, uint i) private pure { for (uint j = i + 1; j < ids.length; j++) { if (ids[i] == ids[j]) revert("duplicate signatures found"); } } function getProofCosts(uint256 proofOption) external pure returns (uint256) { uint256[3] memory feePrices = [uint256(0.01 ether),uint256(0.005 ether),uint256(0.0025 ether)]; return feePrices[proofOption]; } //users can pass in 0,1,2 to get the latest confirmed / priorconfirmed / second prior confirmed statroot and blockhash. //otherwise if they enter a height the proof is free. // returned value is uint32 height bytes32 blockhash and bytes32 stateroot serialized together. function getProof(uint256 proofHeightOptions) public payable returns (bytes memory) { uint256 feePrice; uint256 feeShare; uint256 remainder; // Price for proofs being returned. uint256[3] memory feePrices = [uint256(0.01 ether),uint256(0.005 ether),uint256(0.0025 ether)]; uint256 proposerAndHeight; bytes memory tempBytes = bestForks[0]; assembly { proposerAndHeight := mload(add(tempBytes, FORKS_DATA_CONFIRMED_PROPOSER)) } if (proofHeightOptions < 3) { feePrice = feePrices[proofHeightOptions]; feeShare = (msg.value / VerusConstants.SATS_TO_WEI_STD) / 2; remainder = (msg.value / VerusConstants.SATS_TO_WEI_STD) % 2; require(msg.value == feePrice, "Not enough fee"); // Proposer and notaries get share of fees // any remainder from divide by 2 or equal share to the notaries gets added to proposers share. claimableFees[VerusConstants.VDXF_SYSTEM_NOTARIZATION_NOTARYFEEPOOL] += feeShare; setClaimedFees(bytes32(uint256(uint176(proposerAndHeight))), (feeShare + remainder)); } if (proofHeightOptions == 0) { // if the most recent confirmed is requested just get the height from the bestforks return abi.encodePacked(uint32(proposerAndHeight >> FORKS_DATA_OFFSET_FOR_HEIGHT), proofs[(bytes32(proposerAndHeight >> FORKS_DATA_OFFSET_FOR_HEIGHT))]); } else if(proofHeightOptions == 1 || proofHeightOptions == 2) { // if the prior confirmed or second prior confirmed notarization is required extract the height from the newest confirmed. tempBytes = proofs[(bytes32(proposerAndHeight >> FORKS_DATA_OFFSET_FOR_HEIGHT))]; uint32 previousConfirmedHeight; assembly { previousConfirmedHeight := mload(add(tempBytes, PROOF_HEIGHT_LOCATION)) } if(proofHeightOptions == 1) { return abi.encodePacked(uint32(previousConfirmedHeight), proofs[bytes32(uint256(previousConfirmedHeight))]); } tempBytes = proofs[bytes32(uint256(previousConfirmedHeight))]; uint32 secondPreviousConfirmedHeight; assembly { secondPreviousConfirmedHeight := mload(add(tempBytes, PROOF_HEIGHT_LOCATION)) } return abi.encodePacked(uint32(secondPreviousConfirmedHeight), proofs[bytes32(uint256(secondPreviousConfirmedHeight))]); } else { return abi.encodePacked(uint32(proofHeightOptions), proofs[bytes32(proofHeightOptions)]); } } function setClaimedFees(bytes32 _address, uint256 fees) private { claimableFees[_address] += fees; } function serializeUint32(uint32 number) public pure returns(uint32){ // swap bytes number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8); number = (number >> 16) | (number << 16); return number; } } ================================================ FILE: migrations/1_initial_migration.js ================================================ const Migrations = artifacts.require("Migrations"); module.exports = function(deployer) { deployer.deploy(Migrations); }; ================================================ FILE: migrations/2_deploy_contracts.js ================================================ const Web3 = require('web3'); const setup = require('./setup.js'); var UpgradeManager = artifacts.require("./VerusBridge/UpgradeManager.sol"); var VerusTokenManager = artifacts.require("./VerusBridge/TokenManager.sol"); var VerusSerializer = artifacts.require("./VerusBridge/VerusSerializer.sol"); var VerusNotarizer = artifacts.require("./VerusNotarizer/VerusNotarizer.sol"); var VerusProof = artifacts.require("./MMR/VerusProof.sol"); var VerusCCE = artifacts.require("./VerusBridge/VerusCrossChainExport.sol"); var CreateExports = artifacts.require("./VerusBridge/CreateExports.sol"); var SubmitImports = artifacts.require("./VerusBridge/SubmitImports.sol"); var NotarizationSerializer = artifacts.require("./VerusNotarizer/NotarizationSerializer.sol"); var VerusNotaryTools = artifacts.require("./VerusNotarizer/NotaryTools.sol"); var ExportManager = artifacts.require("./VerusBridge/ExportManager.sol"); var VerusBlake2b = artifacts.require("./MMR/VerusBlake2b.sol"); var VerusMMR = artifacts.require("./MMR/VerusMMR.sol"); var VerusDelegator = artifacts.require("./Main/Delegator.sol"); var Token = artifacts.require("./VerusBridge/Token.sol"); const abi = web3.eth.abi let globalDAI = null; const { returnConstructorCurrencies, arrayofcurrencies, getNotarizerIDS, getDAI, getMKR, getDSRMANAGER, getDAIERC20Address } = setup; module.exports = async function(deployer) { const isTestnet = deployer.network == "development" || deployer.network == "sepolia" || deployer.network == "sepolia-fork"; const currencyConstants = returnConstructorCurrencies(isTestnet); const DAI = getDAI(isTestnet); const MKR = getMKR(isTestnet); const { DSRPOT, DSRJOIN } = getDSRMANAGER(isTestnet); let DAIERC20 = getDAIERC20Address(isTestnet); const launchCurrencies = await getCurrencies(deployer); if (deployer.network == "development") { DAIERC20 = globalDAI; } await deployer.deploy(UpgradeManager); const UpgradeInst = await UpgradeManager.deployed(); await deployer.deploy(VerusBlake2b); await VerusBlake2b.deployed(); await deployer.deploy(VerusSerializer, ...currencyConstants); const serializerInst = await VerusSerializer.deployed(); await deployer.deploy(NotarizationSerializer, ...currencyConstants, DAI, MKR); const notarizationSerializerInst = await NotarizationSerializer.deployed(); await deployer.deploy(VerusTokenManager, ...currencyConstants, DAIERC20) const tokenInst = await VerusTokenManager.deployed(); await deployer.link(VerusBlake2b, VerusNotarizer); await deployer.deploy(VerusNotarizer, ...currencyConstants); const notarizerInst = await VerusNotarizer.deployed(); await deployer.deploy(VerusMMR); await VerusMMR.deployed(); await deployer.link(VerusMMR, VerusProof); await deployer.link(VerusBlake2b, VerusProof); await deployer.deploy(VerusProof, ...currencyConstants); const ProofInst = await VerusProof.deployed(); await deployer.deploy(VerusCCE, ...currencyConstants, DAIERC20, DSRPOT, DSRJOIN); const CCEInst = await VerusCCE.deployed(); await deployer.deploy(ExportManager, ...currencyConstants); const ExportManInst = await ExportManager.deployed(); await deployer.deploy(CreateExports, ...currencyConstants, DAI, DAIERC20); const CreateExportsInst = await CreateExports.deployed(); await deployer.deploy(SubmitImports, ...currencyConstants, DAI, MKR); const SubmitImportsInst = await SubmitImports.deployed(); await deployer.deploy(VerusNotaryTools); const VerusNotaryToolsInst = await VerusNotaryTools.deployed(); const allContracts = [ tokenInst.address, serializerInst.address, ProofInst.address, CCEInst.address, notarizerInst.address, CreateExportsInst.address, VerusNotaryToolsInst.address, ExportManInst.address, SubmitImportsInst.address, notarizationSerializerInst.address, UpgradeInst.address ]; const notarizerIDS = getNotarizerIDS(deployer.network) await deployer.deploy(VerusDelegator, ...notarizerIDS, allContracts); const VerusDelegatorInst = await VerusDelegator.deployed(); await VerusDelegatorInst.launchContractTokens(launchCurrencies); if (deployer.network == "sepolia" || deployer.network == "mainnet" || deployer.network == "mainnet-fork") { await VerusDelegatorInst.replacecontract(CCEInst.address, 3, {gas: 4700000}); // CCE is position 3 in list of contracts } const settingString = "\ndelegatorcontractaddress=" + VerusDelegatorInst.address + "\n\n" + "export const DELEGATOR_ADD = \"" + VerusDelegatorInst.address + "\";"; console.log("\nSettings to be pasted into *.conf file and website constants \n", settingString); }; const getCurrencies = async (deployer) => { // if testnetERC is not null then we are running ganache test and need to replace the DAI address with the testnetERC address. let isTestnet = deployer.network == "development" || deployer.network == "sepolia" || deployer.network == "sepolia-fork" let currencies = arrayofcurrencies(isTestnet); if (deployer.network == "development"){ await deployer.deploy(Token, "DAI (Testnet)", "DAI"); const TokenInst = await Token.deployed(); TokenInst.mint(deployer.networks.development.from, "100000000000000000000000"); console.log("\nDAI DEPLOYED\n", TokenInst.address); currencies[3][1] = TokenInst.address; globalDAI = TokenInst.address; } if (deployer.network == "development" || deployer.network == "sepolia" || deployer.network == "sepolia-fork") { await deployer.deploy(Token, "MKR (Testnet)", "MKR"); //TODO: Replace if there is an offical sepolia ERC20 MKR const TokenInst = await Token.deployed(); TokenInst.mint(deployer.networks[deployer.network].from, "100000000000000000000000"); console.log("\nMKR DEPLOYED\n", TokenInst.address); currencies[4][1] = TokenInst.address; } let data = abi.encodeParameter( 'tuple(address,address,address,uint8,string,string,uint256)[]', currencies); return data; } ================================================ FILE: migrations/setup.js ================================================ const MAPPING_ETHEREUM_OWNED = 1; const MAPPING_VERUS_OWNED = 2; const MAPPING_PARTOF_BRIDGEVETH = 4; const MAPPING_ISBRIDGE_CURRENCY = 8; const MAPPING_ERC20_DEFINITION = 32; // These are the mainnet notaries iaddresses in hex form. const verusMainnetNotariserIDS = []; // These are the equivelent ETH mainnet addresses of the notaries Spending R addresses const verusMainnetNotariserSigner = []; // These are the equivelent ETH mainnet addresses of the notaries Recovery R addresses const verusMainnetNotariserRecovery = []; // These are the notaries Sepolia iaddresses in hex form. const verusSepoliaNotariserIDS = []; // These are the equivelent ETH Sepolia addresses of the notaries Spending R addresses const verusSepoliaNotariserSigner = []; // These are the equivelent ETH Sepolia addresses of the notaries Recovery R addresses const verusSepoliaNotariserRecovery = []; // These are the development notaries iaddresses in hex form. const TestVerusNotariserIDS = [ "0xb26820ee0c9b1276aac834cf457026a575dfce84", "0x51f9f5f053ce16cb7ca070f5c68a1cb0616ba624", "0x65374d6a8b853a5f61070ad7d774ee54621f9638"]; // These are the equivelent ETH development addresses of the notaries Spending R addresses const TestVerusNotariserSigner = [ "0xD010dEBcBf4183188B00cafd8902e34a2C1E9f41", "0xD010dEBcBf4183188B00cafd8902e34a2C1E9f41", "0xD010dEBcBf4183188B00cafd8902e34a2C1E9f41"]; // These are the equivelent ETH development addresses of the notaries Recovery R addresses const TestVerusNotariserRecovery = [ "0xD010dEBcBf4183188B00cafd8902e34a2C1E9f41", "0xD3258AD271066B7a780C68e527A6ee69ecA15b7F", "0x68f56bA248E23b7d5DE4Def67592a1366431d345"]; const getNotarizerIDS = (network) => { if (network == "development"){ return [TestVerusNotariserIDS, TestVerusNotariserSigner, TestVerusNotariserRecovery]; } else if (network == "sepolia" || network == "sepolia-fork"){ return [verusSepoliaNotariserIDS, verusSepoliaNotariserSigner, verusSepoliaNotariserRecovery]; } else if (network == "mainnet" || network == "mainnet-fork"){ return [verusMainnetNotariserIDS, verusMainnetNotariserSigner, verusMainnetNotariserRecovery]; } } // Verus ID's in uint160 format and ERC20s. const id = { mainnet: { VETH: "0x454CB83913D688795E237837d30258d11ea7c752", VRSC: "0x1Af5b8015C64d39Ab44C60EAd8317f9F5a9B6C4C", BRIDGE: "0x0200EbbD26467B866120D84A0d37c82CdE0acAEB", DAI: "0x8b72F1c2D326d376aDd46698E385Cf624f0CA1dA", DAIERC20: "0x6B175474E89094C44Da98b954EedeAC495271d0F", MKR: "0x65b5aac6a4aa0eb656ab6b8812184e7545b6a221", MKRERC20: "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", DAIPOT: "0x197e90f9fad81970ba7976f33cbd77088e5d7cf7", DAIJOIN: "0x9759A6Ac90977b93B58547b4A71c78317f391A28", }, testnet: { VETH: "0x67460C2f56774eD27EeB8685f29f6CEC0B090B00", VRSC: "0xA6ef9ea235635E328124Ff3429dB9F9E91b64e2d", BRIDGE: "0xffEce948b8A38bBcC813411D2597f7f8485a0689", DAI: "0xcce5d18f305474f1e0e0ec1c507d8c85e7315fdf", DAIERC20: "0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844", //NOTE: Testnet DAI on maintestnet is 0xB897f2448054bc5b133268A53090e110D101FFf0 DAIPOT: "0x50672F0a14B40051B65958818a7AcA3D54Bd81Af", DAIJOIN: "0x6a60b7070befb2bfc964F646efDF70388320f4E0", MKR: "0x005005b2b10a897fed36fbd71c878213a7a169bf" , MKRERC20: "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2" // TODO: Change this to testnet MKR }, emptyuint160: "0x0000000000000000000000000000000000000000", emptyuint256: "0x0000000000000000000000000000000000000000000000000000000000000000" } const returnConstructorCurrencies = (isTestnet = false) => { return [ isTestnet ? id.testnet.VETH : id.mainnet.VETH, isTestnet ? id.testnet.BRIDGE : id.mainnet.BRIDGE, isTestnet ? id.testnet.VRSC : id.mainnet.VRSC ] } const getDAI = (isTestnet = false) => { return isTestnet ? id.testnet.DAI : id.mainnet.DAI; } const getMKR = (isTestnet = false) => { return isTestnet ? id.testnet.MKR : id.mainnet.MKR; } const getDSRMANAGER = (isTestnet = false) => { const retval = {DSRJOIN: isTestnet ? id.testnet.DAIJOIN : id.mainnet.DAIJOIN, DSRPOT: isTestnet ? id.testnet.DAIPOT : id.mainnet.DAIPOT} return retval; } const getDAIERC20Address = (isTestnet = false) => { return isTestnet ? id.testnet.DAIERC20 : id.mainnet.DAIERC20; } // currencies that are defined are in this format: // iaddress in hex, ERC20 contract, parent, token options, name, ticker, NFTtokenID. const returnSetupCurrencies = (isTestnet = false) => { //NOTE: IMPORTANT THIS ORDER MATTERS. DO NOT CHANGE IT. ONLY ADD TO THE END. const vrsc = [ isTestnet ? id.testnet.VRSC : id.mainnet.VRSC, id.emptyuint160, id.emptyuint160, MAPPING_VERUS_OWNED + MAPPING_PARTOF_BRIDGEVETH + MAPPING_ERC20_DEFINITION, isTestnet ? "VRSCTEST" : "VRSC", "VRSC", id.emptyuint256]; const bridgeeth = [ isTestnet ? id.testnet.BRIDGE : id.mainnet.BRIDGE, id.emptyuint160, isTestnet ? id.testnet.VRSC : id.mainnet.VRSC, MAPPING_VERUS_OWNED + MAPPING_ISBRIDGE_CURRENCY + MAPPING_ERC20_DEFINITION, "Bridge.vETH", "VBRID", id.emptyuint256]; const veth = [ isTestnet ? id.testnet.VETH : id.mainnet.VETH, id.emptyuint160, isTestnet ? id.testnet.VRSC : id.mainnet.VRSC, MAPPING_ETHEREUM_OWNED + MAPPING_PARTOF_BRIDGEVETH + MAPPING_ERC20_DEFINITION, isTestnet ? "ETH (Testnet)" : "vETH", "vETH", id.emptyuint256]; const dai = [ isTestnet ? id.testnet.DAI : id.mainnet.DAI, isTestnet ? id.testnet.DAIERC20 : id.mainnet.DAIERC20, isTestnet ? id.testnet.VRSC : id.mainnet.VRSC, MAPPING_ETHEREUM_OWNED + MAPPING_PARTOF_BRIDGEVETH + MAPPING_ERC20_DEFINITION, isTestnet ? "DAI (Testnet)" : "DAI.vETH", "DAI", id.emptyuint256]; const mkr = [ isTestnet ? id.testnet.MKR : id.mainnet.MKR, isTestnet ? id.testnet.MKRERC20 : id.mainnet.MKRERC20, isTestnet ? id.testnet.VRSC : id.mainnet.VRSC, MAPPING_ETHEREUM_OWNED + MAPPING_PARTOF_BRIDGEVETH + MAPPING_ERC20_DEFINITION, isTestnet ? "Maker (Testnet)" : "MKR.vETH", "MKR", id.emptyuint256]; // Add new currencies here. return [vrsc, bridgeeth, veth, dai, mkr]; } exports.id = id; exports.getNotarizerIDS = getNotarizerIDS; exports.arrayofcurrencies = returnSetupCurrencies; exports.returnConstructorCurrencies = returnConstructorCurrencies; exports.getDAI = getDAI; exports.getMKR = getMKR; exports.getDSRMANAGER = getDSRMANAGER; exports.getDAIERC20Address = getDAIERC20Address; ================================================ FILE: readme.md ================================================ ## Ethereum Verus contracts Before compilation an account at https://infura.io/ is needed to allow you to connect to the Ethereum network. Create a new Ethereum project and choose rinkeby netowork and get a link that looks like this: https://rinkeby.infura.io/v3/015d792415a734560cd5dbdfeb4 (Dont use this one its invalid) Then edit the file `truffle-config.js` and edit the infura endpoint, also add in your private key to the private key variable. To compile all run: ```shell npm install npm install -g truffle@5.3.14 truffle compile truffle deploy --network rinkeby ``` copy files from ./build/contracts VerusSerializer.json VerusProof.json VerusNotarizer.json VerusInfo.json verusBridge.json To Alan's sub directory directory/abi (these are the Abi files) Run ```shell truffle networks ``` to get a list of the contract addresses: goto your `/Verustest/pbaas/veth/veth.conf` file and copy in the contract addresses from the above list into the appropriate fields. Also copy the VerusBridge Contract address into the index.js of the VerusWebsite Dapp, to update that address. ## Running Truffle Tests To run the automated tests, your private key needs to be set in a .env file in the root folder as ``` GANACHE_KEY=32198a9bbb9.... #32 bye key without the 0x ``` This Key has to be one of the Notaries Spending Keys. Then to run the tests run: ```shell npm run test ``` ## Update 21st July 2023 - Added truffle tests ## Update 11th May 2022 - Truffle version must be no newer than 5.3.14 - Set contracts TokenManger.sol must be initialized with setContracts() after its updated. ================================================ FILE: run.bat ================================================ remixd -s . ================================================ FILE: runtests.sh ================================================ #!/bin/bash # Start ganache-cli in the background ganache-cli -l 1500000000 -p 8545 > "ganache.log" 2>&1 & # Capture the process ID of ganache-cli ganache_pid=$! # Wait for a few seconds to allow ganache-cli to start sleep 3 # Run Truffle tests truffle test # Stop ganache-cli kill $ganache_pid ================================================ FILE: runtruffle.bat ================================================ truffle deploy --network sepolia --reset ================================================ FILE: test/deployed.js ================================================ const VerusDelegator = artifacts.require("../contracts/Main/Delegator.sol"); const VerusSerializer = artifacts.require("../contracts/VerusBridge/VerusSerializer.sol"); const VerusProof = artifacts.require("../contracts/MMR/VerusProof.sol"); const { getNotarizerIDS } = require('../migrations/setup.js') const verusDelegatorAbi = require('../build/contracts/Delegator.json'); const verusSerializerAbi = require('../build/contracts/VerusSerializer.json'); const verusProofAbi = require('../build/contracts/VerusProof.json'); const testNotarization = require('./submitnotarization.js') const reservetransfer = require('./reservetransfer.ts') const { toBase58Check } = require("verus-typescript-primitives"); const ERC721 = require("../build/contracts/ERC721.json"); const { proofinput, invalidComponents } = reservetransfer; const abi = web3.eth.abi const { randomBytes } = require('crypto'); const createUpgradeTuple = (addresses, salt, upgradetype) => { let package = [0, "0x00", "0x00", addresses, upgradetype, salt, "0x0000000000000000000000000000000000000000", 0]; let data = abi.encodeParameter( 'tuple(uint8,bytes32,bytes32,address[],uint8,bytes32,address,uint32)', package); return data; } contract("Verus Contracts deployed tests", async(accounts) => { it("All 6 Currencies Deployed", async() => { const DelegatorInst = await VerusDelegator.deployed(); let tokensList = await DelegatorInst.getTokenList.call(0,0); assert.equal(tokensList.length, 6, "Not all currencies were deployed"); }); it("Notaries Deployed", async() => { const DelegatorInst = await VerusDelegator.deployed(); const notaries = getNotarizerIDS("development")[0] for (let i=0; i< notaries.length; i++){ let firstnotary = await DelegatorInst.notaries.call(i); assert.equal(firstnotary.toLowerCase(), notaries[i].toLowerCase()); } assert.ok(true); }); it("Send 1 ETH to Contract", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; // Get the contract balance before sending ETH const initialBalance = await web3.eth.getBalance(contractAddress); // Send 1 ETH to the contract const sendAmount = web3.utils.toWei("1", "ether"); await web3.eth.sendTransaction({ from: accounts[0], to: contractAddress, value: sendAmount }); // Get the contract balance after sending ETH const finalBalance = await web3.eth.getBalance(contractAddress); // Check if the contract balance increased by 1 ETH const expectedBalance = web3.utils.toBN(initialBalance).add(web3.utils.toBN(sendAmount)); assert.equal(finalBalance.toString(), expectedBalance.toString(), "Contract balance is incorrect after sending ETH"); }); it("Send 1 ETH in Serialized ReserveTransfer to Contract", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; const contractInstance = new web3.eth.Contract(verusDelegatorAbi.abi, contractAddress); // Send 1 ETH to the contract const sendAmount = web3.utils.toWei("1.003", "ether"); const serializedTx = `0x${reservetransfer.prelaunchfundETH.toBuffer().toString('hex')}`; //console.log("reservetransfer transaction " + JSON.stringify(reservetransfer, null, 2)) let reply try { reply = await contractInstance.methods.sendTransferDirect(serializedTx).send({ from: accounts[0], gas: 6000000, value: sendAmount }); // Get the contract balance after sending ETH exportHeights const previousStartHeight = await DelegatorInst.exportHeights.call(0); let reserveimport = await DelegatorInst.getReadyExportsByRange.call(0, reply.blockNumber + 10); assert.equal(reply.blockNumber, reserveimport[0].endHeight, "Endheight should equal insertion height"); } catch(e) { console.log(e.message) assert.ok(false); } }); it("Send 2 ETH in ReserveTransfer to Contract", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; const contractInstance = new web3.eth.Contract(verusDelegatorAbi.abi, contractAddress); // Send 1 ETH to the contract const sendAmount = web3.utils.toWei("2.003", "ether"); const CReserveTransfer = { version: 1, currencyvalue: { currency: "0x67460C2f56774eD27EeB8685f29f6CEC0B090B00", amount: 200000000 }, // currency sending from ethereum flags: 1, feecurrencyid: "0xA6ef9ea235635E328124Ff3429dB9F9E91b64e2d", // fee is vrsctest pre bridge launch, veth or others post. fees: 2000000, destination: { destinationtype: 2, destinationaddress: "0x9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD" }, // destination address currecny is going to destcurrencyid: "0xA6ef9ea235635E328124Ff3429dB9F9E91b64e2d", // destination currency is vrsc on direct. bridge.veth on bounceback destsystemid: "0x0000000000000000000000000000000000000000", // destination system not used secondreserveid: "0x0000000000000000000000000000000000000000" // used as return currency type on bounce back } let reply try { reply = await contractInstance.methods.sendTransfer(CReserveTransfer).send({ from: accounts[0], gas: 6000000, value: sendAmount }); } catch(e) { console.log(e) assert.isTrue(false); } // Get the contract balance after sending ETH exportHeights const previousStartHeight = await DelegatorInst.exportHeights.call(0); let reserveimport = await DelegatorInst.getReadyExportsByRange.call(0, reply.blockNumber + 10); assert.isTrue(true); }); it("Submit accepeted notarization by Notary", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; const contractInstance = new web3.eth.Contract(verusDelegatorAbi.abi, contractAddress); let reply; const votehash= "0x9304c78dd2c478a5cd5841dd751dc16baa320603"; try { reply = await contractInstance.methods.setLatestData(testNotarization.firstNotarization, testNotarization.firsttxid, testNotarization.firstvout, testNotarization.abiencodedSigData).send({ from: accounts[0], gas: 6000000 }); reply = await contractInstance.methods.setLatestData(testNotarization.secondNotarization, testNotarization.secondtxid, testNotarization.secondvout, testNotarization.abiencodedSigData).send({ from: accounts[0], gas: 6000000 }); let test = await contractInstance.methods.rollingUpgradeVotes(0).call(); assert.equal(test.toLowerCase(), votehash, "Vote hash should be equal to the votehash"); test = await contractInstance.methods.rollingUpgradeVotes(1).call(); assert.equal(test.toLowerCase(), votehash, "Vote hash should be equal to the votehash"); test = await contractInstance.methods.rollingUpgradeVotes(2).call(); assert.equal(test.toLowerCase(), "0x0000000000000000000000000000000000000000", "Vote hash should be equal to the null"); let innerreply2 = await contractInstance.methods.getVoteCount(votehash).call(); assert.equal(innerreply2, "2", "Vote count should be 2"); } catch(e) { console.log(e) assert.isTrue(false); } // Get the contract balance after sending ETH exportHeights const notarization = await contractInstance.methods.bestForks(0).call(); const vote = await contractInstance.methods.rollingUpgradeVotes(0).call(); const NotarizationResult = { txid: notarization.substring(66, 130), n: parseInt(notarization.slice(202, 210), 16), hash: notarization.substring(2, 66), }; assert.equal(`0x${NotarizationResult.txid}`, testNotarization.firsttxid, "Txid in best forks does not equal notarization"); }); it("Test Votes", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; const contractInstance = new web3.eth.Contract(verusDelegatorAbi.abi, contractAddress); let randomBuf = randomBytes(32); let outBuffer = Buffer.alloc(1); const TYPE_CONTRACT = 1; outBuffer.writeUInt8(TYPE_CONTRACT); let contractsHex = Buffer.from(''); let contracts = []; // Get the list of current active contracts for (let i = 0; i < 11; i++) { contracts.push(await contractInstance.methods.contracts(i).call()); } const newContract = "0x089D2f1Bdb9DA0eD7350e6224eE40C22cCc20D02"; const newContractType = 2; //replace existing contract with new contract address contracts[newContractType] = newContract; for (let i = 0; i < 11; i++) { contractsHex = Buffer.concat([contractsHex, Buffer.from(contracts[i].slice(2), 'hex')]); } let serialized = Buffer.concat([contractsHex, outBuffer, randomBuf]); let hashedContractPackage = web3.utils.keccak256(serialized).toString('hex').slice(26,66); let reply; // Get the contract balance after sending ETH exportHeights const vote = await contractInstance.methods.rollingUpgradeVotes(0).call(); // NOTE: This test requires a modified delegator to be able to set the votes /*************************************************** */ await contractInstance.methods.modifyvote(hashedContractPackage, 24).send({ from: accounts[0], gas: 6000000 }); /**** Insert this into the delegator ******************************************* */ // function modifyvote(address hashValue, uint numOfvotes) external { // for (uint i = 1; i < numOfvotes+1; i++) // { // rollingUpgradeVotes[i] = hashValue; // } /******************************************************** */ let count = 0; let innerreply = await contractInstance.methods.getVoteCount(hashedContractPackage).call(); count = parseInt(innerreply); assert.equal(count, 24, "error in vote amount"); let upgradetup try { upgradetup = createUpgradeTuple(contracts, "0x"+randomBuf.toString('hex'), 1) reply = await contractInstance.methods.upgradeContracts(upgradetup).send({ from: accounts[0], gas: 6000000 }); assert.isTrue(false, "Should not have been able to upgrade"); } catch(e) { } await contractInstance.methods.modifyvote(hashedContractPackage, 26).send({ from: accounts[0], gas: 6000000 }); count = 0; let innerreply2 = await contractInstance.methods.getVoteCount(hashedContractPackage).call(); count = parseInt(innerreply2); assert.equal(count, 26, "error in vote amount"); try { reply = await contractInstance.methods.upgradeContracts(upgradetup).send({ from: accounts[0], gas: 6000000 }); const vote = await contractInstance.methods.rollingUpgradeVotes(0).call(); const rollingIndex = await contractInstance.methods.rollingVoteIndex().call(); assert.equal(rollingIndex, 0, "Rolling index should be 0"); assert.equal(vote, "0x0000000000000000000000000000000000000000", "Vote should be reset"); } catch(e) { console.log(e) assert.isTrue(false, "Should have been able to upgrade"); } for (let i = 0; i < 11; i++) { const replaced = await contractInstance.methods.contracts(i).call(); if (i == 2) { assert.equal(replaced, newContract, "Contract was not replaced"); } else { assert.equal(replaced, contracts[i], "Contract was not replaced"); } } }); it("Test Serializer with bounceback sendTransfer", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); const prelaunchtx = `0x${reservetransfer.prelaunchfundETH.toBuffer().toString('hex')}`; const bounceback = `0x${reservetransfer.bounceback.toBuffer().toString('hex')}`; let reply; try { reply = await contractInstance.methods.deserializeTransfer(bounceback).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.secondreserveid.slice(2),'hex'), 102), reservetransfer.bounceback.second_reserve_id , "secondreserveid does not equal transaction"); }); it("Deserialize two Reserve transfers", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const twoTransfersSerialized = Buffer.concat([reservetransfer.twoReserveTransfers[0].toBuffer(), reservetransfer.twoReserveTransfers[1].toBuffer()]).toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${twoTransfersSerialized}`, 2).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } const txOne = new web3.utils.BN(reply.tempTransfers[0].currencyAndAmount).toString('hex').slice(7); assert.equal(toBase58Check(Buffer.from(txOne,'hex'), 102), reservetransfer.twoReserveTransfers[0].reserve_values.value_map.keys().next().value , "transfer currency does not equal transaction"); }); it("Deserialize a Reserve transfer with a mapped ERC721", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc721transfer = reservetransfer.erc721transferETH.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc721transfer}${erc721transfer}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ" , "transfer currency does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0x39Ec448b891c476e166b3C3242A90830DB556661" , "ERC721 does not equal transaction"); assert.equal(reply.launchTxs[0].flags, "129" , "ERC721 does not equal transaction"); assert.equal(reply.launchTxs[0].tokenID, 255 , "ERC721 TokenID does not equal the correct (first Currency Export)"); }); it("Deserialize a Reserve transfer with a verus owned ERC721", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc721transfer = reservetransfer.erc721transferVerus.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc721transfer}${erc721transfer}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ" , "transfer currency (chad7) does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0x0000000000000000000000000000000000000000" , "ERC721 does not equal an empty address"); assert.equal(reply.launchTxs[0].flags, "130" , "Ethereum mapped currency does not have the correct flags "); }); it("Deserialize a Reserve transfer with a verus owned ERC20", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc20verustoken = reservetransfer.erc20verustoken.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc20verustoken}${erc20verustoken}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ" , "transfer currency (chad7) does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0x0000000000000000000000000000000000000000" , "ERC20 does not equal verus ERC20 NFT address"); assert.equal(reply.launchTxs[0].flags, "34" , "Ethereum mapped currency does not have the correct flags "); }); it("Deserialize a Reserve transfer with a ETH owned ERC20", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc20ETHtoken = reservetransfer.erc20ETHtoken.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc20ETHtoken}${erc20ETHtoken}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ" , "transfer currency (chad7) does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0xB897f2448054bc5b133268A53090e110D101FFf0" , "ERC20 does not equal DAI address (first Currency Export)"); assert.equal(reply.launchTxs[1].ERCContract, "0xB897f2448054bc5b133268A53090e110D101FFf0" , "ERC20 does not equal DAI address (second Currency Export)"); assert.equal(reply.launchTxs[0].flags, "33" , "Ethereum mapped currency does not have the correct flags "); }); it("Deserialize a Reserve transfer with a ERC1155 Verus mapped nft", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc1155VerusNFT = reservetransfer.erc1155VerusNFT.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc1155VerusNFT}${erc1155VerusNFT}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "iAwycBuMcPJii45bKNTEfSnD9W9iXMiKGg" , "transfer currency (id2) does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0xF7F25BFC8a4E4a4413243Cc5388e5a056cb4235b" , "ERC1155 does not equal the correct address (first Currency Export)"); assert.equal(reply.launchTxs[1].ERCContract, "0xF7F25BFC8a4E4a4413243Cc5388e5a056cb4235b" , "ERC1155 does not equal the correct (second Currency Export)"); assert.equal(reply.launchTxs[0].tokenID, 255 , "ERC1155 TokenID does not equal the correct (first Currency Export)"); assert.equal(reply.launchTxs[1].tokenID, 255 , "ERC1155 TokenID does not equal the correct (second Currency Export)"); assert.equal(reply.launchTxs[0].flags, "17" , "Ethereum mapped currency does not have the correct flags "); }); it("Deserialize a Reserve transfer with a ERC1155 to token mapping", async () => { const VerusSerializerInst = await VerusSerializer.deployed(); const contractAddress = VerusSerializerInst.address; const contractInstance = new web3.eth.Contract(verusSerializerAbi.abi, contractAddress); // convert the two reserveTransfers to a single hex string const erc1155Token = reservetransfer.erc1155Token.toBuffer().toString('hex'); let reply; try { reply = await contractInstance.methods.deserializeTransfers(`0x${erc1155Token}${erc1155Token}${reservetransfer.twoReserveTransfers[0].toBuffer().toString('hex')}`, 3).call(); // console.log(reply) } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(toBase58Check(Buffer.from(reply.launchTxs[0].iaddress.slice(2), 'hex'), 102), "iAwycBuMcPJii45bKNTEfSnD9W9iXMiKGg" , "transfer currency (id2) does not equal transaction"); assert.equal(reply.launchTxs[0].ERCContract, "0xF7F25BFC8a4E4a4413243Cc5388e5a056cb4235b" , "ERC1155 does not equal the correct (first Currency Export)"); assert.equal(reply.launchTxs[1].ERCContract, "0xF7F25BFC8a4E4a4413243Cc5388e5a056cb4235b" , "ERC1155 does not equal the correct (second Currency Export)"); assert.equal(reply.launchTxs[0].tokenID, 255 , "ERC1155 TokenID does not equal the correct (first Currency Export)"); assert.equal(reply.launchTxs[1].tokenID, 255 , "ERC1155 TokenID does not equal the correct (second Currency Export)"); assert.equal(reply.launchTxs[0].flags, "65" , "Ethereum mapped currency does not have the correct flags "); }); it("Check Verus ERC721 has launched", async () => { const DelegatorInst = await VerusDelegator.deployed(); const contractAddress = DelegatorInst.address; const contractInstance = new web3.eth.Contract(verusDelegatorAbi.abi, contractAddress); let tokensList = await contractInstance.methods.tokenList(0).call(); const NFTContract = new web3.eth.Contract(ERC721.abi, tokensList); let reply; try { reply = await NFTContract.methods.name().call(); ; } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(reply, "VerusNFT" , "Verus ERC721 name does not equal transaction"); }); it("Prove components", async () => { const VerusProofInst = await VerusProof.deployed(); const contractAddress = VerusProofInst.address; const contractInstance = new web3.eth.Contract(verusProofAbi.abi, contractAddress); let reply; invalidComponents try { reply = await contractInstance.methods.checkProof("0x0000000000000000000000000000000000000000000000000000000000000000",proofinput[0].partialtransactionproof.components[0].elProof).call(); } catch(e) { console.log(e.message) assert.isTrue(false); } assert.equal(reply, "0x29b5437905fbf6cd87bb5964bff0306b1e5870ef375e4622b438012177dc7261"); try { reply = await contractInstance.methods.checkProof("0x0000000000000000000000000000000000000000000000000000000000000000", invalidComponents).call(); } catch(e) { assert.equal(e.message, "VM Exception while processing transaction: revert") return; } assert.isTrue(false); }); }); ================================================ FILE: test/helper.js ================================================ const Web3 = require("web3") // import web3 v1.0 constructor // use globally injected web3 to find the currentProvider and wrap with web3 v1.0 const getWeb3 = () => { const myWeb3 = new Web3(web3.currentProvider) return myWeb3 } // assumes passed-in web3 is v1.0 and creates a function to receive contract name const getContractInstance = (web3) => (contractName) => { const artifact = artifacts.require(contractName) // globally injected artifacts helper const deployedAddress = artifact.networks[artifact.network_id].address const instance = new web3.eth.Contract(artifact.abi, deployedAddress) return instance } module.exports = { getWeb3, getContractInstance } ================================================ FILE: test/reservetransfer.ts ================================================ const { CurrencyValueMap, ReserveTransfer, TransferDestination } = require("verus-typescript-primitives"); //const BigNumber = require("verus-typescript-primitives/dist/utils/types/Bignumber"); var BN = require('bn.js'); const DEST_PKH = 2 const DEST_ID = 4 const DEST_ETH = 9 const DEST_REGISTERCURRENCY = 6 const FLAG_DEST_AUX = 64 const FLAG_DEST_GATEWAY = 128 const VALID = 1 const CONVERT = 2 const PRECONVERT = 4 const CROSS_SYSTEM = 0x40 const IMPORT_TO_SOURCE = 0x200 const RESERVE_TO_RESERVE = 0x400 const CURRENCY_EXPORT = 0x2000 const prelaunchfundETH = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", new BN(100000000, 10)] ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), fee_currency_id: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq", // fee currency vrsctest fee_amount: new BN(2000000, 10), transfer_destination: new TransferDestination({ type: new BN(2, 10), destination_bytes: Buffer.from("9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD", 'hex'), fees: new BN(0, 10) }), dest_currency_id: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq" }) const bounceback = new ReserveTransfer({ // The bridge currency has to be launched for this TX ETH -> VRSCTEST back to ETH address values: new CurrencyValueMap({ value_map: new Map([ ["iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", new BN(100000000, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID + CONVERT + RESERVE_TO_RESERVE, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_ETH + FLAG_DEST_AUX + FLAG_DEST_GATEWAY, 10), destination_bytes: Buffer.from("9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD", 'hex'), gateway_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", fees: new BN(300000, 10), aux_dests:[new TransferDestination({ type: new BN(DEST_PKH, 10), destination_bytes: Buffer.from("9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD", 'hex')})] }), dest_currency_id: "iSojYsotVzXz4wh2eJriASGo6UidJDDhL2", second_reserve_id: "iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2yq" }) //run definecurrency '{"name":"chad7","options":2080,"systemid":"veth","parent":"vrsctest","currencies":["VRSCTEST"], //"launchsystemid":"vrsctest","nativecurrencyid":{"type":10,"address": {"contract": "0x39Ec448b891c476e166b3C3242A90830DB556661", //"tokenid":"0x00000000000000000000000000000000000000000000000000000000000000ff"}},"maxpreconversion":[0],"initialsupply":0,"proofprotocol":3}' const verusReserveTransfer = new ReserveTransfer({ // The bridge currency has to be launched for this TX ETH -> VRSCTEST back to ETH address values: new CurrencyValueMap({ value_map: new Map([ ["iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", new BN(100000000, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_ETH + FLAG_DEST_AUX, 10), destination_bytes: Buffer.from("9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD", 'hex'), aux_dests:[new TransferDestination({ type: new BN(DEST_PKH, 10), destination_bytes: Buffer.from("9bB2772Aa50ec96ce1305D926B9CC29b7c402bAD", 'hex')})] }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) //run definecurrency '{"name":"chad7","options":2080,"preallocations":[{"chad7@":0.00000001}],"maxpreconversion":[0]}' const erc721transferETH = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020080000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2d67460c2f56774ed27eeb8685f29f6cec0b090b0001000000030000000a3439ec448b891c476e166b3c3242a90830db55666100000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000008a8e0d00000000000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100000000000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const erc721transferVerus = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020080000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2da6ef9ea235635e328124ff3429db9f9e91b64e2d0100000001000000000000000000000000000000000000000000000000008a9351000000000000000000012c0d13af98a412ad79e77cfdee70bac119b054fa0100000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100000000000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const erc20verustoken = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020000000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2da6ef9ea235635e328124ff3429db9f9e91b64e2d0100000001000000000000000000000000000000000000000000000000008a9451000000000000000000012c0d13af98a412ad79e77cfdee70bac119b054fa0080ca396124000000000000000000000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const erc20ETHtoken = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["i7VSq7gm2xe7vWnjK9SvJvTUvy5rcLfozZ", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020000000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2d67460c2f56774ed27eeb8685f29f6cec0b090b0001000000030000000914b897f2448054bc5b133268a53090e110d101fff000000000000000000000000000000000000000008a953e00000000000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100e1f505000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) // const erc1155VerusNFT = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["iAwycBuMcPJii45bKNTEfSnD9W9iXMiKGg", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020080000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2d67460c2f56774ed27eeb8685f29f6cec0b090b0001000000030000004a34f7f25bfc8a4e4a4413243cc5388e5a056cb4235b00000000000000000000000000000000000000000000000000000000000000ff012301210200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000008a9f0000000000000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100000000000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const erc1155Token = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["iAwycBuMcPJii45bKNTEfSnD9W9iXMiKGg", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(300000, 10), // 0.003 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020000000a6ef9ea235635e328124ff3429db9f9e91b64e2d056368616437a6ef9ea235635e328124ff3429db9f9e91b64e2d67460c2f56774ed27eeb8685f29f6cec0b090b0001000000030000004914f7f25bfc8a4e4a4413243cc5388e5a056cb4235b012301210200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000008a9f1b00000000000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100e1f505000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const testErcVerus = new ReserveTransfer({ values: new CurrencyValueMap({ value_map: new Map([ ["iAwycBuMcPJii45bKNTEfSnD9W9iXMiKGg", new BN(0, 10)] //swap 1 ETH ]), multivalue: false }), version: new BN(1, 10), flags: new BN(VALID + CROSS_SYSTEM + CURRENCY_EXPORT, 10), // fee_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", // fee currency veth fee_amount: new BN(540000, 10), // 0.0054 ETH FEE SATS (8 decimal places) transfer_destination: new TransferDestination({ type: new BN(DEST_REGISTERCURRENCY, 10), destination_bytes: Buffer.from("0100000020000000a6ef9ea235635e328124ff3429db9f9e91b64e2d0865726332306d6170a6ef9ea235635e328124ff3429db9f9e91b64e2d67460c2f56774ed27eeb8685f29f6cec0b090b0001000000030000000914b897f2448054bc5b133268a53090e110d101fff000000000000000000000000000000000000000008a0600000000000000000000000000000000000001a6ef9ea235635e328124ff3429db9f9e91b64e2d000100e1f505000000000001000000000000000001000000000000000001000000000000000000000000000000a49faec70003f98800", 'hex'), }), dest_currency_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm", dest_system_id: "iCtawpxUiCc2sEupt7Z4u8SDAncGZpgSKm" }) const twoReserveTransfers = [verusReserveTransfer, verusReserveTransfer]; const proofinput = [ { "height":341, "txid":"0x75ea17c9654f23733a03d25a9c0387956e9c8fdef47ce840474c6513b5a63843", "txoutnum":0, "exportinfo":{ "version":1, "flags":2, "sourcesystemid":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "hashtransfers":"0xd7281d5bd3e008a0dd2ba12b8760fbe3215087cd3a483f6c0398507f690f7904", "destinationsystemid":"0xa6ef9ea235635e328124ff3429db9f9e91b64e2d", "destinationcurrencyid":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "sourceheightstart":1, "sourceheightend":2, "numinputs":1, "totalamounts":[ { "currency":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "amount":2030000 } ], "totalfees":[ { "currency":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "amount":30000 } ], "totalburned":[ { "currency":"0x0000000000000000000000000000000000000000", "amount":0 } ], "rewardaddress":{ }, "firstinput":1 }, "partialtransactionproof":{ "version":1, "typeC":2, "txproof":[ { "branchType":2, "proofSequence":{ "CMerkleBranchBase":2, "nIndex":1, "nSize":3, "extraHashes":0, "branch":[ "0x2c916711a7143497062940f543daf3dcd5900b7fd7da1f8b7523cd6b2071c18a", "0x0000000000000000000000000000000000000000000000000000000000000000" ], "txHeight":1 } }, { "branchType":2, "proofSequence":{ "CMerkleBranchBase":2, "nIndex":0, "nSize":2, "extraHashes":0, "branch":[ "0x54e88762b07838958306fe87ce052ba5f0d8b297c8fbbf53223db5a881000000" ], "txHeight":0 } }, { "branchType":3, "proofSequence":{ "CMerkleBranchBase":3, "nIndex":2594, "nSize":352, "extraHashes":1, "branch":[ "0xc7eae60100000000000000000000000000000000000000000000000000000000", "0xd6dff527c354ecc7168330ef7b9966a50910afb002d4a126591d115f055f7c1b", "0xa302f60300000000000000000000000000000000000000000000000000000000", "0x1daa88fa256b6e879c2797485a2278ea82332cafca0693e22f95a69977dfd78a", "0x1ee6960700000000000000000000000087c7a49db76a14000000000000000000", "0xd605679f5a0272e17503ab04646569402b8a092160a5aa3a4dd71b3e6976ef72", "0xb2aac80f0000000000000000000000001c9f037f589a1f000000000000000000", "0xefc72b3aa715c29af607b122c16d9f086cc5c0b683d3f17fcdc559549d685c14", "0x0bd4e220000000000000000000000000eec57944234640000000000000000000", "0x3a673fe4ee3c97a889846a3fddfe3f8c7e149bafe4481740ceae656427fdebdf", "0x4203f73400000000000000000000000079959cd36e4a9a000000000000000000", "0xc4954a000980058dda5450b30e46ba72649c44df2df8e558e467e3c8475682aa", "0x3241720c020000000000000000000000605acd8192f316020000000000000000" ], "txHeight":341 } } ], "components":[ { "elType":1, "elIdx":0, "elVchObj":"0x75ea17c9654f23733a03d25a9c0387956e9c8fdef47ce840474c6513b5a63843010400000085202f890200000001000000000000000000000000000000690100000000000000000000", "elProof":[ { "branchType":2, "proofSequence":{ "CMerkleBranchBase":2, "nIndex":0, "nSize":6, "extraHashes":0, "branch":[ "0xf36ab323b97f31ec40f2634c0f3d9bd2b96ada1a46db7b14ca03e85b0651449e", "0x025253b337536886a30c1e3e0cb8f4d7868a0b2772f7dd447039911ab939f310", "0x4156f8d4d3394072fb13b25574becb10a92b830e9aec0e3fe97cf7e1d3a7a814" ], "txHeight":0 } } ] }, { "elType":2, "elIdx":0, "elVchObj":"0x77fe97c08b114257c7a897e3a3da356033ab612dd1706fd1b193bb74a33a29ac04000000ffffffff", "elProof":[ { "branchType":2, "proofSequence":{ "CMerkleBranchBase":2, "nIndex":1, "nSize":6, "extraHashes":0, "branch":[ "0x8aee4690d988f4a81b6575ea25a25e03381105b4312ca0b710d0bbd3e71ef1ed", "0x025253b337536886a30c1e3e0cb8f4d7868a0b2772f7dd447039911ab939f310", "0x4156f8d4d3394072fb13b25574becb10a92b830e9aec0e3fe97cf7e1d3a7a814" ], "txHeight":1 } } ] }, { "elType":4, "elIdx":0, "elVchObj":"0x0000000000000000f91a04030001011452047d0db35c330271aae70bedce996b5239ca5ccc4cda04030c01011452047d0db35c330271aae70bedce996b5239ca5c4cbe01008000a6ef9ea235635e328124ff3429db9f9e91b64e2dd7281d5bd3e008a0dd2ba12b8760fbe3215087cd3a483f6c0398507f690f790467460c2f56774ed27eeb8685f29f6cec0b090b0067460c2f56774ed27eeb8685f29f6cec0b090b0002144e6f51cf16700e4edb9390ed42912e3498ec26050100000001000000811e8154000167460c2f56774ed27eeb8685f29f6cec0b090b00b0f91e00000000000167460c2f56774ed27eeb8685f29f6cec0b090b00b0f91e00000000000075", "elProof":[ { "branchType":2, "proofSequence":{ "CMerkleBranchBase":2, "nIndex":3, "nSize":6, "extraHashes":0, "branch":[ "0x5a1d4bd7301a5da460525637c0ae9042bc3dc7c7c6eaab350bfbb1f9d0e56a7c", "0xbb038091d3f2fac7bec99c72d776f506cc13667820f92b1a3726c70773c0db99" ], "txHeight":5 } } ] } ] }, "transfers":[ { "version":1, "flags":65, "crosssystem":true, "feecurrencyid":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "fees":30000, "destination":{ "destinationaddress":"0x37245c7f865b5c1b6f1db81523ccf3626df625bc", "destinationtype":9 }, "secondreserveid":"0x0000000000000000000000000000000000000000", "currencyvalue":{ "currency":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "amount":2000000 }, "destcurrencyid":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00", "destsystemid":"0x67460c2f56774ed27eeb8685f29f6cec0b090b00" } ], "serializedTransfers":"0x0167460c2f56774ed27eeb8685f29f6cec0b090b00f988004167460c2f56774ed27eeb8685f29f6cec0b090b0080e930091437245c7f865b5c1b6f1db81523ccf3626df625bc67460c2f56774ed27eeb8685f29f6cec0b090b0067460c2f56774ed27eeb8685f29f6cec0b090b00" } ] const invalidComponents = [] module.exports.invalidComponents = invalidComponents; module.exports.proofinput = proofinput; module.exports.prelaunchfundETH = prelaunchfundETH; module.exports.bounceback = bounceback; module.exports.twoReserveTransfers = twoReserveTransfers; module.exports.erc721transferETH = erc721transferETH; module.exports.erc721transferVerus = erc721transferVerus; module.exports.erc20verustoken = erc20verustoken; module.exports.erc20ETHtoken = erc20ETHtoken; module.exports.erc1155VerusNFT = erc1155VerusNFT; module.exports.erc1155Token = erc1155Token; module.exports.testErcVerus = testErcVerus; ================================================ FILE: test/submitnotarization.js ================================================ const notarization = { "serializednotarization": "0x02810c0414b26820ee0c9b1276aac834cf457026a575dfce8467460c2f56774ed27eeb8685f29f6cec0b090b000100300067460c2f56774ed27eeb8685f29f6cec0b090b0001a6ef9ea235635e328124ff3429db9f9e91b64e2d010000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000100000000000000000100ca9a3b0000000001000000000000000001000000000000000001000000000100000000000000001e2a8f006e9c813278b839b0a7735f1c7413bd207b1f50da0d711df5c3fdad8942f579eb030000000000000000000000000000000000000000000000000000000000000000000000f70300000201001000a6ef9ea235635e328124ff3429db9f9e91b64e2d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000300ffece948b8a38bbcc813411d2597f7f8485a068903a6ef9ea235635e328124ff3429db9f9e91b64e2dcce5d18f305474f1e0e0ec1c507d8c85e7315fdf67460c2f56774ed27eeb8685f29f6cec0b090b000356a0fc0155a0fc0155a0fc010300000000000000000000000000000000000000000000000095ddb082e7ff000095ddb082e7ff000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000032b010000000000002c010000000000002c0100000000000003000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000300000000000000000000000003000000000000000000000000000000000000000000000000020100020067460c2f56774ed27eeb8685f29f6cec0b090b001e2a8f007cacf2d8eeba89f59e15ca55f2291bb6da95666c4eaf18a650096ab8a37bfb5bbcac80f52ddc01bae758027294cdaf8fc23ccc9f5b4e5553252957b8fcd0b04b70a4a4000000000000000000000000000000000000000000000000000000000000ca9a3b0000000001000100a6ef9ea235635e328124ff3429db9f9e91b64e2dfb030000aeb1d621fbd2cd4196df4153536259d023af456b6061803ecf9eac3dc72e2ff3804f4ee6397bdd22715f4f7eeb0574be3d5615441a96dcd03a95a67a83000000599cd6410c000000000000000000000010ce4cffb495fb08000000000000000000", "txid": "0x52815121074277380353401aa6c2e9b49dbf8ba6e1655e2584768d0a03f949cb", "voutnum": 1, "abiencodedSigData": "0x00000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000290074f4702781dfb580504d7855b4ccff19c684ce7922353848975e2b574295f595505bb5050279f4fa214604dfec66df5ab4c96ef723f78709cc2728e2a114d000000000000000000000000000000000000000000000000000000000000000271533f04489e7ee23ad6fc04302ddc4bbf53d5272d3f2dc8d6bcc9ea8876b04d5441758e6041a009409a3c402d331815649fa64ebefe107c0f3a62b4f1197f3f0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000040a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000051f9f5f053ce16cb7ca070f5c68a1cb0616ba62400000000000000000000000065374d6a8b853a5f61070ad7d774ee54621f9638", firstNotarization: "0x02850c42147079115f779ee5305b172f7a4a59f806d9585704011609149304c78dd2c478a5cd5841dd751dc16baa32060367460c2f56774ed27eeb8685f29f6cec0b090b000100300067460c2f56774ed27eeb8685f29f6cec0b090b0001a6ef9ea235635e328124ff3429db9f9e91b64e2d010000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000100000000000000000100ca9a3b00000000010000000000000000010000000000000000010000000001000000000000000007af99000a73da358361041165ac1eed2202c75cfdc1cdd8ef488bc9c740cadec9a97ace01000000ae79c282816a9544f1903e732b5dd773b23f899494edf3b11c3ce30a6a8145fbdaae99000201001000a6ef9ea235635e328124ff3429db9f9e91b64e2d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001003100ffece948b8a38bbcc813411d2597f7f8485a068904a6ef9ea235635e328124ff3429db9f9e91b64e2dcce5d18f305474f1e0e0ec1c507d8c85e7315fdf67460c2f56774ed27eeb8685f29f6cec0b090b00005005b2b10a897fed36fbd71c878213a7a169bf0440787d0140787d0140787d0140787d0104c5f5939b160000000046c323000000005dfd620000000000008d380c0100000095ddb082e7ff0000a2f9c09ff68354aad84fcd833200000000000000000000000000000000000000000000000000000400000000000000000000000000000000404b4c0000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000004d4aa0d01000000000000000000000000000000000000000000000000000000000402c7050000000000230900000000000009000000000000008744000000000000041ae203000000000024060000000000001000000000000000102e000000000000042a4e0000000000000000000000000000e20400000000000000000000000000000440787d0140787d0140787d0140787d010400000000000000000000000000000000e2040000000000000000000000000000020100020067460c2f56774ed27eeb8685f29f6cec0b090b0007af9900386cf5d968f5eb903e1a77b4f7a9d1faf80e5a4892fea5e63eb97180db280730df3efd595783e8b0722e0ad96ea5ad41c7efd7af1b361dc95553e82df3eb4e2870a4a4000000000000000000000000000000000000000000000000000000000000ca9a3b0000000001000100a6ef9ea235635e328124ff3429db9f9e91b64e2dbe0a000034c3abce06ed4967e051f50704323dc74caf3204ec5f97191b835c229aaa195b507fa4f0cbcaa537403d0c37b9ad9d9483d7cbf6605322982e17faaa2200000098cbf3053f0000000000000000000000d808cf95c81f191a00000000000000000075", secondNotarization: "0x02850c4414b26820ee0c9b1276aac834cf457026a575dfce84011609149304c78dd2c478a5cd5841dd751dc16baa32060367460c2f56774ed27eeb8685f29f6cec0b090b000100300067460c2f56774ed27eeb8685f29f6cec0b090b0001a6ef9ea235635e328124ff3429db9f9e91b64e2d010000000001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000100000000000000000100000000000000000100ca9a3b00000000010000000000000000010000000000000000010000000001000000000000000041af990096990d7ce0e37b363e19f50f4aa22dea1dc9df6a848302fbcc89d2ce2389d0b20100000099acb75826ca17ad7905fc6d1b8121e5081c6b74713d4afdfd48b46fc23a17eb07af99000201001000a6ef9ea235635e328124ff3429db9f9e91b64e2d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001003100ffece948b8a38bbcc813411d2597f7f8485a068904a6ef9ea235635e328124ff3429db9f9e91b64e2dcce5d18f305474f1e0e0ec1c507d8c85e7315fdf67460c2f56774ed27eeb8685f29f6cec0b090b00005005b2b10a897fed36fbd71c878213a7a169bf0440787d0140787d0140787d0140787d0104c5f5939b160000000046c323000000005dfd620000000000008d380c0100000095ddb082e7ff0000a2f9c09ff68354aad84fcd833200000000000000000000000000000000000000000000000000000400000000000000000000000000000000404b4c0000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000004d4aa0d01000000000000000000000000000000000000000000000000000000000402c7050000000000230900000000000009000000000000008744000000000000041ae203000000000024060000000000001000000000000000102e000000000000042a4e0000000000000000000000000000e20400000000000000000000000000000440787d0140787d0140787d0140787d010400000000000000000000000000000000e2040000000000000000000000000000020100020067460c2f56774ed27eeb8685f29f6cec0b090b0041af99000a3aa46e03ee81626a87ec1c943d33de5b26fb0c3e601d6c10dbbfe40083633c9bf527d9992c497b5a588a580be5ea74688893cb1c65de77ea563029d0e0433070a4a4000000000000000000000000000000000000000000000000000000000000ca9a3b0000000001000100a6ef9ea235635e328124ff3429db9f9e91b64e2dca0a00001104a6b414548bea96cbae0ae0f3ed8806c42be0aa629e35205cb75158de70038280a75339d83befaf27620e0d2b4b1e2c980f4e9810538033d42003160000008d33a35d3f000000000000000000000002a3d70a13e9321a00000000000000000075", firsttxid: "0x96990d7ce0e37b363e19f50f4aa22dea1dc9df6a848302fbcc89d2ce2389d0b2", secondtxid: "0xa198ad06ad783a38c5a392860cffc12d2f94455eb24b7112b8c3adb1335568e8", firstvout: 0, secondvout: 1 } module.exports = notarization;