Skip to content
Open

Dev #75

Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
911bf22
Added fees
bohdan-titan Sep 12, 2023
4af0597
Merge pull request #71 from Lumerin-protocol/feat/fee
alex-sandrk Sep 13, 2023
bae2f51
feat: new proxy api
alex-sandrk Sep 13, 2023
877c41c
Merge pull request #72 from Lumerin-protocol/feat/new-proxy
alex-sandrk Sep 13, 2023
8131ca9
update smart-contracts
alex-sandrk Sep 14, 2023
d42cd03
Fix gas fee
bohdan-titan Sep 14, 2023
4b9e3fd
Update version
bohdan-titan Sep 14, 2023
beea422
feat: contract version
alex-sandrk Sep 21, 2023
4d63f8a
Merge pull request #73 from Lumerin-protocol/feature/contract-version
lsheva Sep 21, 2023
b194ab1
Stg bugfixes (#74)
srt0422 Sep 23, 2023
3d681f1
feat: check port
alex-sandrk Sep 25, 2023
23cc390
use overloaded web3 instance instead of single provider. required fo…
srt0422 Sep 28, 2023
6d77bc7
polling for new blocks
alex-sandrk Oct 3, 2023
16795ff
new retry implementation web3
alex-sandrk Oct 9, 2023
a00b29b
avoid infinite loop
alex-sandrk Oct 9, 2023
928b7e4
check nullable
alex-sandrk Oct 9, 2023
b945126
Merge pull request #77 from Lumerin-protocol/feature/new-retry-web3
alex-sandrk Oct 9, 2023
e40769b
update ecies-geth lib
alex-sandrk Nov 6, 2023
cf02876
release 1.0.75
alex-sandrk Nov 6, 2023
2607918
fix: cycling on too many requests
alex-sandrk Nov 14, 2023
b59b6e8
1.0.77
alex-sandrk Nov 14, 2023
f2b10f8
1.0.78
alex-sandrk Nov 14, 2023
6265c85
1.0.78
alex-sandrk Nov 14, 2023
0a3f046
feat: 1.0.79
alex-sandrk Nov 14, 2023
8ba706c
handle specific error
alex-sandrk Nov 14, 2023
32dd3a9
feat: handle all nodes 429
alex-sandrk Nov 16, 2023
9010bc4
1.0.82
alex-sandrk Nov 17, 2023
3027f84
fix: use explorerApiURLs instead of mapping chainID to explorer api url
shev-titan Nov 21, 2023
20b933c
feat: additional error handling
alex-sandrk Dec 7, 2023
12e12cf
Update auto-tag.yml
alex-sandrk Dec 7, 2023
56d073d
Profit target change
bohdan-titan Jan 2, 2024
fc2b427
Added v2
bohdan-titan Jan 9, 2024
21ff51e
fix: do not pay for contract update
alex-sandrk Jan 12, 2024
025de27
Merge branch 'dev' into feature/profit-target
alex-sandrk Jan 12, 2024
4f2686c
remove comments
alex-sandrk Jan 12, 2024
bf38a1c
Merge branch 'feature/profit-target' of github.com:Lumerin-protocol/W…
alex-sandrk Jan 12, 2024
5d40dee
Merge pull request #79 from Lumerin-protocol/feature/profit-target
alex-sandrk Jan 15, 2024
319b61a
allow negative profit target
alex-sandrk Jan 17, 2024
f007876
refresh contract data after purchase
alex-sandrk Jan 24, 2024
b200545
fix: slow ui updates
alex-sandrk Feb 2, 2024
418bfb6
fix: additional error handling, more logs, more retries
alex-sandrk Feb 8, 2024
ba596f8
group if condition
alex-sandrk Feb 8, 2024
00e96ce
Merge pull request #80 from Lumerin-protocol/fix/web3-retries
abs2023 Feb 8, 2024
7fccd3d
fix: handle additional public node error
alex-sandrk Mar 4, 2024
ecc41ba
feat: auto-close + indexer
alex-sandrk Mar 26, 2024
795483c
Merge pull request #81 from Lumerin-protocol/feature/auto-close
alex-sandrk Mar 26, 2024
0207e1c
feat: dynamic block reward
alex-sandrk Apr 16, 2024
4ee8460
Merge pull request #82 from Lumerin-protocol/feature/dynamic-block-re…
alex-sandrk Apr 16, 2024
ec72246
Removed whitelist check
bohdan-titan Jan 16, 2025
b2e85eb
Added new plugin
bohdan-titan Jan 24, 2025
ec92156
feat: validator registry contract integration
lsheva Feb 2, 2025
536a357
Merge pull request #84 from Lumerin-protocol/feature/validator-registry
shev-titan Feb 2, 2025
e9dc828
fix: move multicall to deps
lsheva Feb 2, 2025
8b85e97
Merge pull request #85 from Lumerin-protocol/feature/validator-registry
shev-titan Feb 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/auto-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: butlerlogic/action-autotag@stable
- uses: butlerlogic/action-autotag@1.1.2
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
with:
strategy: package
strategy: package
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lumerin/wallet-core",
"version": "1.0.64",
"version": "1.1.2",
"author": {
"name": "Lumerin",
"email": "developer@lumerin.io",
Expand Down Expand Up @@ -34,12 +34,13 @@
"abi-decoder": "^2.4.0",
"axios": "0.21.1",
"axios-cookiejar-support": "1.0.1",
"bottleneck": "^2.19.5",
"chalk": "^2.4.2",
"cheerio": "^1.0.0-rc.12",
"contracts-js": "github:Lumerin-protocol/contracts-js#v0.0.24",
"contracts-js": "github:Lumerin-protocol/contracts-js#v0.1.0",
"cross-port-killer": "^1.4.0",
"debug": "4.1.1",
"ecies-geth": "^1.6.2",
"ecies-geth": "^1.7.0",
"electron-log": "^4.4.8",
"ethereumjs-wallet": "1.0.1",
"ip": "^1.1.8",
Expand Down
156 changes: 63 additions & 93 deletions src/plugins/contracts/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const logger = require('../../logger')
const { encrypt } = require('ecies-geth')
const { Implementation } = require('contracts-js')
const { remove0xPrefix, add65BytesPrefix } = require('./helpers')
const { ContractEventsListener } = require('./events-listener')
const ethereumWallet = require('ethereumjs-wallet').default

/**
Comment on lines 3 to 11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The code imports several modules and utilities at the top of the file. However, there is a potential security risk with the direct use of ethereumjs-wallet. Using this package involves handling private keys directly, which can be risky if not managed securely. It's recommended to ensure that private keys are never exposed in logs, error messages, or through any form of insecure storage. Consider implementing additional security measures such as encryption-at-rest for stored keys and secure key management practices.

Expand All @@ -19,7 +18,7 @@ async function _loadContractInstance(
try {
const implementationContract = Implementation(web3, implementationAddress)
const contract = await implementationContract.methods
.getPublicVariables()
.getPublicVariablesV2()
.call()
const stats = await implementationContract.methods.getStats().call()

Comment on lines 21 to 27
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The code performs asynchronous operations (implementationContract.methods.getPublicVariablesV2().call() and implementationContract.methods.getStats().call()) without any error handling mechanism. If any of these operations fail, for example, due to a network issue or an invalid contract address, it will result in an unhandled promise rejection, potentially causing runtime issues. It's recommended to wrap these asynchronous operations in a try-catch block to handle any errors that may occur, ensuring the application can gracefully handle the failure scenario.

Expand All @@ -39,10 +38,14 @@ async function _loadContractInstance(

const {
_state: state,
_price: price, // cost to purchase the contract
_limit: limit, // max th provided
_speed: speed, // th/s of contract
_length: length, // duration of the contract in seconds
_terms: {
_price: price, // cost to purchase the contract
_limit: limit, // max th provided
_speed: speed, // th/s of contract
_length: length, // duration of the contract in seconds
_version: version,
_profitTarget: profitTarget
},
_startingBlockTimestamp: timestamp, // timestamp of the block at moment of purchase
_buyer: buyer, // wallet address of the purchasing party
_seller: seller, // wallet address of the selling party
Comment on lines 38 to 54
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The destructuring assignment from contract in lines 39-56 extracts multiple properties and nested properties. This approach, while concise, can lead to runtime errors if the contract object does not strictly conform to the expected structure (e.g., if _terms is undefined, accessing nested properties like _price will throw a TypeError). To enhance the robustness of the code, consider adding checks or using optional chaining (?.) to safely access nested properties.

Comment on lines 38 to 54
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The destructuring assignment from contract in lines 39-56 extracts multiple properties and nested properties. This approach, while concise, can lead to runtime errors if the contract object does not strictly conform to the expected structure (e.g., if _terms is undefined, accessing nested properties like _price will throw a TypeError). To enhance the robustness of the code, consider adding checks or using optional chaining (?.) to safely access nested properties.

Comment on lines 41 to 54
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The destructuring assignment from contract in lines 41-54 extracts multiple properties and nested properties. This approach, while concise, can lead to runtime errors if the contract object does not strictly conform to the expected structure (e.g., if _terms is undefined, accessing nested properties like _price will throw a TypeError). To enhance the robustness of the code, consider adding checks or using optional chaining (?.) to safely access nested properties.

Recommended Change:

const { _state: state, _terms: terms = {} } = contract;
const { _price: price, _limit: limit, _speed: speed, _length: length, _version: version, _profitTarget: profitTarget } = terms;

This change ensures that if _terms is undefined, it defaults to an empty object, preventing runtime errors when accessing nested properties.

Expand All @@ -60,6 +63,8 @@ async function _loadContractInstance(
speed: data._speed,
length: data._length,
limit: data._limit,
version: data._version,
profitTarget: data._profitTarget
}
}

Comment on lines 66 to 73
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The conditional block from lines 59-69 checks if walletAddress, hasFutureTerms, and if seller equals walletAddress to decide whether to fetch futureTerms. This logic could be simplified and made more readable by combining these conditions into a single if-statement using logical AND operators. Additionally, consider handling the scenario where implementationContract.methods.futureTerms().call() might fail due to network issues or contract state problems by wrapping it in a try-catch block.

Comment on lines 66 to 73
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The conditional block from lines 66-73 checks if walletAddress, hasFutureTerms, and if seller equals walletAddress to decide whether to fetch futureTerms. This logic could be simplified and made more readable by combining these conditions into a single if-statement using logical AND operators. Additionally, consider handling the scenario where implementationContract.methods.futureTerms().call() might fail due to network issues or contract state problems by wrapping it in a try-catch block.

Recommended Change:

if (walletAddress && hasFutureTerms && seller === walletAddress) {
  try {
    const data = await implementationContract.methods.futureTerms().call();
    futureTerms = {
      price: data._price,
      speed: data._speed,
      length: data._length,
      limit: data._limit,
      version: data._version,
      profitTarget: data._profitTarget
    };
  } catch (error) {
    logger.error('Failed to fetch future terms:', error);
  }
}

This change ensures that errors during the fetching of future terms are caught and logged, preventing potential crashes and improving the reliability of the application.

Expand All @@ -84,9 +89,12 @@ async function _loadContractInstance(
hasFutureTerms,
futureTerms,
history: buyerHistory,
version,
profitTarget
},
}
} catch (err) {
logger.error(err)
logger.error(
'Error when trying to load Contracts by address in the Implementation contract: ',
err
Expand All @@ -96,132 +104,76 @@ async function _loadContractInstance(
}

/**
* @param {import('web3').default} web3
* @param {import('web3').default} web3Subscriptionable
* @param {import('contracts-js').LumerinContext} lumerin
* @param {import('contracts-js').CloneFactoryContext} cloneFactory
* @param {string[]} addresses
* @param {string} walletAddress
*/
async function getContracts(
web3,
web3Subscriptionable,
lumerin,
cloneFactory,
addresses,
walletAddress
) {
return Promise.all(
addresses.map((address) =>
getContract(
web3,
web3Subscriptionable,
lumerin,
cloneFactory,
address,
walletAddress
)
)
)
}

/**
* @param {import('web3').default} web3
* @param {import('web3').default} web3Subscriptionable
* @param {import('contracts-js').LumerinContext} lumerin
* @param {string} contractId
* @param {string} walletAddress
*/
async function getContract(
web3,
web3Subscriptionable,
lumerin,
cloneFactory,
contractId,
walletAddress
) {
const contractEventsListener = ContractEventsListener.getInstance()
const contractInfo = await _loadContractInstance(
web3,
contractId,
walletAddress
)

contractEventsListener.addContract(
contractInfo.data.id,
Implementation(web3Subscriptionable, contractId),
walletAddress
)
return contractInfo.data
const getMarketplaceFee = (cloneFactory) => async () => {
return await cloneFactory.methods.marketplaceFee().call()
}

/**
* @param {import('web3').default} web3
* @param {import('contracts-js').CloneFactoryContext} cloneFactory
*/
function createContract(web3, cloneFactory, plugins) {
function createContract(web3, cloneFactory) {
if (!web3) {
logger.error('Not a valid Web3 instance')
return
Comment on lines +120 to 123
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The function createContract checks if the web3 parameter is not falsy and logs an error if it is. However, it does not stop the execution of the function after logging the error, which could lead to runtime errors when it tries to use web3 later in the function. It's recommended to return from the function immediately after logging the error to prevent further execution when the web3 instance is not valid.

Comment on lines +120 to 123
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The function createContract checks if the web3 parameter is not falsy and logs an error if it is. However, it does not stop the execution of the function after logging the error, which could lead to runtime errors when it tries to use web3 later in the function. It's recommended to return from the function immediately after logging the error to prevent further execution when the web3 instance is not valid. This issue has already been identified, so consider this a reinforcement of the need for a fix.

}

return async function (params) {
// const { gasPrice } = await plugins.wallet.getGasPrice()
let {
price,
limit = 0,
speed,
duration,
sellerAddress,
profit = 0,
validatorAddress = '0x0000000000000000000000000000000000000000',
privateKey,
} = params

const isWhitelisted = await cloneFactory.methods
.checkWhitelist(sellerAddress)
.call()
if (!isWhitelisted) {
throw new Error('seller is not whitelisted')
}

const tempWallet = ethereumWallet.fromPrivateKey(
Buffer.from(remove0xPrefix(privateKey), 'hex')
)
const pubKey = tempWallet.getPublicKey()

const account = web3.eth.accounts.privateKeyToAccount(privateKey)
web3.eth.accounts.wallet.create(0).add(account)
const marketplaceFee = await cloneFactory.methods.marketplaceFee().call()

const gas = await cloneFactory.methods
.setCreateNewRentalContract(
.setCreateNewRentalContractV2(
price,
limit,
speed,
duration,
+profit,
validatorAddress,
pubKey.toString('hex')
)
.estimateGas({
from: sellerAddress,
value: marketplaceFee,
})

return cloneFactory.methods
.setCreateNewRentalContract(
.setCreateNewRentalContractV2(
price,
limit,
speed,
duration,
+profit,
validatorAddress,
pubKey.toString('hex')
)
.send({ from: sellerAddress, gas })
.send({ from: sellerAddress, gas, value: marketplaceFee })
}
}

/**
* @param {import('web3').default} web3
*/
function cancelContract(web3) {
function cancelContract(web3, cloneFactory) {
if (!web3) {
logger.error('Not a valid Web3 instance')
return
Expand All @@ -230,7 +182,6 @@ function cancelContract(web3) {
return async function (params) {
const {
walletAddress,
gasLimit = 1000000,
contractId,
privateKey,
closeOutType,
Expand All @@ -239,17 +190,21 @@ function cancelContract(web3) {
const account = web3.eth.accounts.privateKeyToAccount(privateKey)
web3.eth.accounts.wallet.create(0).add(account)

const marketplaceFee = await cloneFactory.methods.marketplaceFee().call()

const gas = await Implementation(web3, contractId)
.methods.setContractCloseOut(closeOutType)
.estimateGas({
from: walletAddress,
value: marketplaceFee,
})

return await Implementation(web3, contractId)
.methods.setContractCloseOut(closeOutType)
.send({
from: walletAddress,
gas,
value: marketplaceFee,
})
}
}
Expand All @@ -267,7 +222,6 @@ function setContractDeleteStatus(web3, cloneFactory, onUpdate) {
return async function (params) {
const {
walletAddress,
gasLimit = 3000000,
contractId,
privateKey,
deleteContract,
Expand Down Expand Up @@ -295,9 +249,6 @@ function setContractDeleteStatus(web3, cloneFactory, onUpdate) {
from: walletAddress,
gas,
})
onUpdate(contractId, walletAddress).catch((err) =>
logger.error(`Failed to refresh after setContractDeadStatus: ${err}`)
)
return result
}
}
Expand All @@ -311,8 +262,8 @@ function setContractDeleteStatus(web3, cloneFactory, onUpdate) {
*/
function purchaseContract(web3, cloneFactory, lumerin) {
return async (params) => {
const { walletId, contractId, url, privateKey, price } = params
const sendOptions = { from: walletId, gas: 1_000_000 }
const { walletId, contractId, url, privateKey, price, version } = params
const sendOptions = { from: walletId }

//getting pubkey from contract to be purchased
const implementationContract = Implementation(web3, contractId)
Expand All @@ -335,23 +286,42 @@ function purchaseContract(web3, cloneFactory, lumerin) {
throw new Error('Contract is deleted already')
}

const totalPrice = (+price + price * 0.01).toString()
const increaseAllowanceEstimate = await lumerin.methods
.increaseAllowance(cloneFactory.options.address, price)
.estimateGas({
from: walletId,
})

await lumerin.methods
.increaseAllowance(cloneFactory.options.address, totalPrice)
.send(sendOptions)
.increaseAllowance(cloneFactory.options.address, price)
.send({
from: walletId,
gas: increaseAllowanceEstimate,
})

const marketplaceFee = await cloneFactory.methods.marketplaceFee().call()

const purchaseGas = await cloneFactory.methods
.setPurchaseRentalContract(contractId, ciphertext.toString('hex'))
.setPurchaseRentalContract(
contractId,
ciphertext.toString('hex'),
version
)
.estimateGas({
from: sendOptions.from,
value: marketplaceFee,
})

const purchaseResult = await cloneFactory.methods
.setPurchaseRentalContract(contractId, ciphertext.toString('hex'))
.setPurchaseRentalContract(
contractId,
ciphertext.toString('hex'),
version
)
.send({
...sendOptions,
gas: purchaseGas,
value: marketplaceFee,
})

logger.debug('Finished puchase transaction', purchaseResult)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The function purchaseContract in lines 262-327 performs several blockchain-related operations without handling potential exceptions specifically. For instance, calls like lumerin.methods.increaseAllowance(...).send() and cloneFactory.methods.setPurchaseRentalContract(...).send() could fail due to reasons such as out of gas errors, invalid nonce, or network issues. It's crucial to wrap these operations in try-catch blocks to handle these exceptions gracefully, providing error feedback and preventing the function from crashing the application.

Expand All @@ -375,20 +345,21 @@ function editContract(web3, cloneFactory, lumerin) {
limit = 0,
speed,
duration,
profit = 0
} = params
const sendOptions = { from: walletId, gas: 1_000_000 }
const sendOptions = { from: walletId }

const account = web3.eth.accounts.privateKeyToAccount(privateKey)
web3.eth.accounts.wallet.create(0).add(account)
Comment on lines 421 to 359
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The function defined in these lines is creating a new account and adding it to the wallet but it does not check if an account with the same private key already exists in the wallet. This could lead to duplicate accounts in the wallet. It's recommended to check if an account with the same private key already exists before adding it to the wallet.


const editGas = await cloneFactory.methods
.setUpdateContractInformation(contractId, price, limit, speed, duration)
.setUpdateContractInformationV2(contractId, price, limit, speed, duration, +profit)
.estimateGas({
from: sendOptions.from,
})

});
const editResult = await cloneFactory.methods
.setUpdateContractInformation(contractId, price, limit, speed, duration)
.setUpdateContractInformationV2(contractId, price, limit, speed, duration, +profit)
.send({
...sendOptions,
gas: editGas,
Comment on lines 345 to 371
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In the function editContract from lines 345-365, there is a potential risk of reentrancy attacks because it sends a transaction (cloneFactory.methods.setUpdateContractInformationV2(...).send()) that could potentially call back into any other function in this contract. To mitigate this risk, consider implementing a reentrancy guard that prevents the function from being called while it is still executing.

Expand All @@ -399,11 +370,10 @@ function editContract(web3, cloneFactory, lumerin) {
}

module.exports = {
getContracts,
getContract,
createContract,
cancelContract,
purchaseContract,
setContractDeleteStatus,
editContract,
getMarketplaceFee,
}
Loading