diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e66601e1..5b51e5fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,10 @@ jobs: path: ~/.npm key: ${{ runner.os }}-test-unit-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} restore-keys: ${{ runner.os }}-test-unit-${{ env.cache-name }}- - + # Env var expansion workaround + # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable + - name: Set ADDRESS_FILE + run: echo "ADDRESS_FILE=${HOME}/.ocean/ocean-contracts/artifacts/address.json" >> $GITHUB_ENV - name: Checkout Barge uses: actions/checkout@v3 with: @@ -57,6 +60,8 @@ jobs: working-directory: ${{ github.workspace }}/barge run: | bash -x start_ocean.sh --no-aquarius --no-elasticsearch --no-provider --no-dashboard 2>&1 > start_ocean.log & + env: + CONTRACTS_VERSION: v1.1.3 - run: npm ci - name: Wait for contracts deployment working-directory: ${{ github.workspace }}/barge diff --git a/package-lock.json b/package-lock.json index 3bf5e5fa..2f7870d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.8", "license": "Apache-2.0", "dependencies": { - "@oceanprotocol/contracts": "^1.0.0", + "@oceanprotocol/contracts": "^1.1.3", "bignumber.js": "^9.0.2", "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", @@ -2589,9 +2589,9 @@ } }, "node_modules/@oceanprotocol/contracts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.0.0.tgz", - "integrity": "sha512-rDCIooe1WHipLejuGhx2Wv/88SB7bWrN3+XHCWxXyPKTmmSQsgxKZPPzbIVBQ0ESChQZqGSBBJyqErqwwW4eBw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.3.tgz", + "integrity": "sha512-pn0rm4QKF8sVfDeJVlt18TV4Qj5oGgR/qQNO7O0GO2DQ3q8KHXRS15uRjmLTr5wW1kGcCHcTqEndXEEC7Elzkw==" }, "node_modules/@octokit/auth-token": { "version": "3.0.1", @@ -19327,9 +19327,9 @@ } }, "@oceanprotocol/contracts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.0.0.tgz", - "integrity": "sha512-rDCIooe1WHipLejuGhx2Wv/88SB7bWrN3+XHCWxXyPKTmmSQsgxKZPPzbIVBQ0ESChQZqGSBBJyqErqwwW4eBw==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-1.1.3.tgz", + "integrity": "sha512-pn0rm4QKF8sVfDeJVlt18TV4Qj5oGgR/qQNO7O0GO2DQ3q8KHXRS15uRjmLTr5wW1kGcCHcTqEndXEEC7Elzkw==" }, "@octokit/auth-token": { "version": "3.0.1", diff --git a/package.json b/package.json index 66123bdb..5c79b658 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "web3": "^1.7.4" }, "dependencies": { - "@oceanprotocol/contracts": "^1.0.0", + "@oceanprotocol/contracts": "^1.1.3", "bignumber.js": "^9.0.2", "cross-fetch": "^3.1.5", "crypto-js": "^4.1.1", diff --git a/src/contracts/index.ts b/src/contracts/index.ts index 65895b8b..7c5bb899 100644 --- a/src/contracts/index.ts +++ b/src/contracts/index.ts @@ -6,3 +6,6 @@ export * from './Router' export * from './Datatoken' export * from './NFT' export * from './NFTFactory' +export * from './ve/VeOcean' +export * from './ve/VeFeeDistributor' +export * from './ve/VeAllocate' diff --git a/src/contracts/ve/VeAllocate.ts b/src/contracts/ve/VeAllocate.ts new file mode 100644 index 00000000..912b9e15 --- /dev/null +++ b/src/contracts/ve/VeAllocate.ts @@ -0,0 +1,117 @@ +import { AbiItem } from 'web3-utils' +import veAllocateABI from '@oceanprotocol/contracts/artifacts/contracts/ve/veAllocate.sol/veAllocate.json' +import { calculateEstimatedGas, sendTx } from '../../utils' +import { SmartContractWithAddress } from '../SmartContractWithAddress' +import { ReceiptOrEstimate } from '../../@types' +/** + * Provides an interface for veOcean contract + */ +export class VeAllocate extends SmartContractWithAddress { + getDefaultAbi(): AbiItem | AbiItem[] { + return veAllocateABI.abi as AbiItem[] + } + + /** + * set a specific percentage of veOcean to a specific nft + * Maximum allocated percentage is 10000, so 1% is specified as 100 + * @param {String} userAddress user address + * @param {String} amount Percentage used + * @param {String} nft NFT address to allocate to + * @param {String} chainId chainId of NFT + * @return {Promise} + */ + public async setAllocation( + userAddress: string, + amount: string, + nft: string, + chainId: number, + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.setAllocation, + amount, + nft, + chainId + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 1, + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.setAllocation, + amount, + nft, + chainId + ) + return >trxReceipt + } + + /** + * set specific percetage of veOcean to multiple nfts + * Maximum allocated percentage is 10000, so 1% is specified as 100 + * @param {String} userAddress user address + * @param {String[]} amount Array of percentages used + * @param {String[]} nft Array of NFT addresses + * @param {String[]} chainId Array of chainIds + * @return {Promise} + */ + public async setBatchAllocation( + userAddress: string, + amount: string[], + nft: string[], + chainId: number[], + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.setBatchAllocation, + amount, + nft, + chainId + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 1, + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.setBatchAllocation, + amount, + nft, + chainId + ) + return >trxReceipt + } + + /** Get totalAllocation for address + * @param {String} userAddress user address + * @return {Promise} + */ + public async getTotalAllocation(userAddress: string): Promise { + const allocation = await this.contract.methods.getTotalAllocation(userAddress).call() + return allocation + } + + /** Get getveAllocation for address, nft, chainId + * @param {String} userAddress user address + * @param {String} nft NFT address to allocate to + * @param {String} chainId chainId of NFT + * @return {Promise} + */ + public async getVeAllocation( + userAddress: string, + nft: string, + chainId: string + ): Promise { + const allocation = await this.contract.methods + .getveAllocation(userAddress, nft, chainId) + .call() + return allocation + } +} diff --git a/src/contracts/ve/VeFeeDistributor.ts b/src/contracts/ve/VeFeeDistributor.ts new file mode 100644 index 00000000..956fd4a3 --- /dev/null +++ b/src/contracts/ve/VeFeeDistributor.ts @@ -0,0 +1,74 @@ +import { AbiItem } from 'web3-utils' +import veFeeABI from '@oceanprotocol/contracts/artifacts/contracts/ve/veFeeDistributor.vy/veFeeDistributor.json' +import { calculateEstimatedGas, sendTx } from '../../utils' +import { SmartContractWithAddress } from '../SmartContractWithAddress' +import { ReceiptOrEstimate } from '../../@types' +/** + * Provides an interface for veOcean contract + */ +export class VeFeeDistributor extends SmartContractWithAddress { + getDefaultAbi(): AbiItem | AbiItem[] { + return veFeeABI.abi as AbiItem[] + } + + /** + * Claim fees for `userAddress` + * Each call to claim look at a maximum of 50 user veOCEAN points. + For accounts with many veOCEAN related actions, this function + may need to be called more than once to claim all available + fees. In the `Claimed` event that fires, if `claim_epoch` is + less than `max_epoch`, the account may claim again + * @param {String} userAddress user address + * @return {Promise} + */ + public async claim( + userAddress: string, + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas(userAddress, this.contract.methods.claim) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 20000, + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.claim + ) + return >trxReceipt + } + + /** + * Make multiple fee claims in a single call + Used to claim for many accounts at once, or to make + multiple claims for the same address when that address + has significant veOCEAN history + * @param {String} fromUserAddress user address that sends the tx + * @param {String} addresses array of addresses to claim + * @return {Promise} + */ + public async claimMany( + fromUserAddress: string, + addresses: string[], + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas( + fromUserAddress, + this.contract.methods.claim_many, + addresses + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + fromUserAddress, + estGas + 20000, + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.claim_many, + addresses + ) + return >trxReceipt + } +} diff --git a/src/contracts/ve/VeOcean.ts b/src/contracts/ve/VeOcean.ts new file mode 100644 index 00000000..6e28c41a --- /dev/null +++ b/src/contracts/ve/VeOcean.ts @@ -0,0 +1,222 @@ +import { AbiItem } from 'web3-utils' +import veOceanABI from '@oceanprotocol/contracts/artifacts/contracts/ve/veOCEAN.vy/veOCEAN.json' +import { calculateEstimatedGas, sendTx } from '../../utils' +import { SmartContractWithAddress } from '../SmartContractWithAddress' +import { ReceiptOrEstimate } from '../../@types' +/** + * Provides an interface for veOcean contract + */ +export class VeOcean extends SmartContractWithAddress { + getDefaultAbi(): AbiItem | AbiItem[] { + return veOceanABI.abi as AbiItem[] + } + + /** + * Deposit `amount` tokens for `userAddress` and lock until `unlockTime` + * @param {String} userAddress user address + * @param {String} amount Amount of tokens to be locked + * @param {Number} unlockTime Timestamp for unlock + * @return {Promise} + */ + public async lockTokens( + userAddress: string, + amount: string, + unlockTime: number, + estimateGas?: G + ): Promise> { + const amountFormatted = await this.amountToUnits(await this.getToken(), amount) + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.create_lock, + amountFormatted, + unlockTime + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 20000, // sometimes, it's not enough + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.create_lock, + amountFormatted, + unlockTime + ) + return >trxReceipt + } + + /** + * Deposit `amount` tokens for `toAddress` and add to the existing lock + * Anyone (even a smart contract) can deposit for someone else, but cannot extend their locktime and deposit for a brand new user + * @param {String} fromUserAddress user address that sends the tx + * @param {String} toAddress user address to deposit for + * @param {String} amount Amount of tokens to be locked + * @return {Promise} + */ + public async depositFor( + fromUserAddress: string, + toAddress: string, + amount: string, + estimateGas?: G + ): Promise> { + const amountFormatted = await this.amountToUnits(await this.getToken(), amount) + const estGas = await calculateEstimatedGas( + fromUserAddress, + this.contract.methods.deposit_for, + toAddress, + amountFormatted + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + fromUserAddress, + estGas + 20000, // sometimes, it's not enough + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.deposit_for, + toAddress, + amountFormatted + ) + return >trxReceipt + } + + /** + * Deposit `amount` additional tokens for `userAddress` without modifying the unlock time + * @param {String} userAddress user address that sends the tx + * @param {String} amount Amount of tokens to be locked + * @return {Promise} + */ + public async increaseAmount( + userAddress: string, + amount: string, + estimateGas?: G + ): Promise> { + const amountFormatted = await this.amountToUnits(await this.getToken(), amount) + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.increase_amount, + amountFormatted + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 20000, // sometimes, it's not enough + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.increase_amount, + amountFormatted + ) + return >trxReceipt + } + + /** + * Extend the unlock time for `userAddress` to `unlockTime` + * @param {String} userAddress user address that sends the tx + * @param {Number} unlockTime Timestamp for new unlock time + * @return {Promise} + */ + public async increaseUnlockTime( + userAddress: string, + unlockTime: number, + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.increase_unlock_time, + unlockTime + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 20000, // sometimes, it's not enough + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.increase_unlock_time, + unlockTime + ) + return >trxReceipt + } + + /** + * Withdraw all tokens for `userAddress` + * @param {String} userAddress user address that sends the tx + * @return {Promise} + */ + public async withdraw( + userAddress: string, + estimateGas?: G + ): Promise> { + const estGas = await calculateEstimatedGas( + userAddress, + this.contract.methods.withdraw + ) + if (estimateGas) return >estGas + + // Invoke function of the contract + const trxReceipt = await sendTx( + userAddress, + estGas + 1, + this.web3, + this.config?.gasFeeMultiplier, + this.contract.methods.withdraw + ) + return >trxReceipt + } + + /** Get voting power for address + * @param {String} userAddress user address + * @return {Promise} + */ + public async getVotingPower(userAddress: string): Promise { + const balance = await this.contract.methods.balanceOf(userAddress).call() + return balance + } + + /** Get locked balance + * @param {String} userAddress user address + * @return {Promise} + */ + public async getLockedAmount(userAddress: string): Promise { + const balance = await this.contract.methods.locked(userAddress).call() + const balanceFormated = await this.unitsToAmount( + await this.getToken(), + balance.amount + ) + + return balanceFormated + } + + /** Get untilLock for address + * @param {String} userAddress user address + * @return {Promise} + */ + public async lockEnd(userAddress: string): Promise { + const untilLock = await this.contract.methods.locked__end(userAddress).call() + return untilLock + } + + /** Get total supply + * @return {Promise} + */ + public async totalSupply(): Promise { + const supplyFormated = await this.unitsToAmount( + await this.getToken(), + await this.contract.methods.totalSupply().call() + ) + return supplyFormated + } + + /** Get token + * @return {Promise} + */ + public async getToken(): Promise { + const tokenAddress = await this.contract.methods.token().call() + return tokenAddress + } +} diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index ac6950be..feef4687 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -171,7 +171,7 @@ const algoDdoWithNoTimeout = { image: 'ubuntu', tag: 'latest', checksum: - 'sha256:42ba2dfce475de1113d55602d40af18415897167d47c2045ec7b6d9746ff148f' + 'sha256:2d7ecc9c5e08953d586a6e50c29b91479a48f69ac1ba1f9dc0420d18a728dfc5' } } }, @@ -213,7 +213,7 @@ const algoDdoWith1mTimeout = { image: 'ubuntu', tag: 'latest', checksum: - 'sha256:42ba2dfce475de1113d55602d40af18415897167d47c2045ec7b6d9746ff148f' + 'sha256:2d7ecc9c5e08953d586a6e50c29b91479a48f69ac1ba1f9dc0420d18a728dfc5' } } }, diff --git a/test/unit/veOcean.test.ts b/test/unit/veOcean.test.ts new file mode 100644 index 00000000..f3146ccf --- /dev/null +++ b/test/unit/veOcean.test.ts @@ -0,0 +1,218 @@ +import { assert } from 'chai' +import { AbiItem } from 'web3-utils' +import { web3, getTestConfig, getAddresses } from '../config' +import { + Config, + approve, + VeOcean, + VeFeeDistributor, + sendTx, + calculateEstimatedGas, + NftFactory, + VeAllocate +} from '../../src' + +describe('veOcean tests', async () => { + let config: Config + let addresses: any + let nftFactory + let veOcean: VeOcean + let veFeeDistributor: VeFeeDistributor + let veAllocate: VeAllocate + let ownerAccount: string + let Alice: string + let Bob: string + let nft1, nft2, nft3 + let chainId + + before(async () => { + config = await getTestConfig(web3) + }) + + it('initialize accounts', async () => { + addresses = getAddresses() + const accounts = await web3.eth.getAccounts() + chainId = await web3.eth.getChainId() + ownerAccount = accounts[0] + Alice = accounts[1] + Bob = accounts[2] + const minAbi = [ + { + constant: false, + inputs: [ + { name: 'to', type: 'address' }, + { name: 'value', type: 'uint256' } + ], + name: 'mint', + outputs: [{ name: '', type: 'bool' }], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + } + ] as AbiItem[] + const tokenContract = new web3.eth.Contract(minAbi, addresses.Ocean) + const estGas = await calculateEstimatedGas( + ownerAccount, + tokenContract.methods.mint, + Alice, + web3.utils.toWei('1000') + ) + await sendTx( + ownerAccount, + estGas, + web3, + 1, + tokenContract.methods.mint, + Alice, + web3.utils.toWei('1000') + ) + await sendTx( + ownerAccount, + estGas, + web3, + 1, + tokenContract.methods.mint, + Bob, + web3.utils.toWei('1000') + ) + veOcean = new VeOcean(addresses.veOCEAN, web3) + veFeeDistributor = new VeFeeDistributor(addresses.veFeeDistributor, web3) + veAllocate = new VeAllocate(addresses.veAllocate, web3) + nftFactory = new NftFactory(addresses.ERC721Factory, web3) + }) + + it('Alice should lock 100 Ocean', async () => { + // since we can only lock once, we test if tx fails or not + // so if there is already a lock, skip it + const currentBalance = await veOcean.getLockedAmount(Alice) + const currentLock = await veOcean.lockEnd(Alice) + const amount = '100' + await approve(web3, config, Alice, addresses.Ocean, addresses.veOCEAN, amount) + const timestamp = Math.floor(Date.now() / 1000) + const unlockTime = timestamp + 7 * 86400 + + if (parseInt(currentBalance) > 0 || currentLock > 0) { + // we already have some locked tokens, so our transaction should fail + try { + await veOcean.lockTokens(Alice, amount, unlockTime) + assert(false, 'This should fail!') + } catch (e) { + // do nothing + } + } else { + const estGas = await veOcean.lockTokens(Alice, amount, unlockTime, true) + console.log('Estimate gas for lockTokens:' + estGas) + const tx = await veOcean.lockTokens(Alice, amount, unlockTime) + // check events + assert(tx.events.Deposit.returnValues[0] === Alice) + assert(tx.events.Deposit.returnValues[1] === web3.utils.toWei(amount)) + assert(tx.events.Deposit.returnValues[2] > 0) // we cannot compare it to the actual untiLock, because contract will round it to weeks + assert(tx.events.Supply.returnValues[1] > tx.events.Supply.returnValues[0]) // supply has increased + } + }) + + it('Alice should increase the lock time', async () => { + const currentLock = await veOcean.lockEnd(Alice) + const newLock = parseInt(String(currentLock)) + 7 * 86400 + await veOcean.increaseUnlockTime(Alice, newLock) + const newCurrentLock = await veOcean.lockEnd(Alice) + assert(newCurrentLock > currentLock, 'Lock time should change"') + }) + + it('Alice should increase the locked amount', async () => { + const currentBalance = await veOcean.getLockedAmount(Alice) + const currentLock = await veOcean.lockEnd(Alice) + const amount = '200' + await approve(web3, config, Alice, addresses.Ocean, addresses.veOCEAN, amount) + await veOcean.increaseAmount(Alice, amount) + const newCurrentBalance = await veOcean.getLockedAmount(Alice) + const newCurrentLock = await veOcean.lockEnd(Alice) + assert(newCurrentLock === currentLock, 'Lock time should not change') + assert( + newCurrentBalance > currentBalance, + 'Amount error:' + newCurrentBalance + ' shoud be > than ' + currentBalance + ) + }) + + it('Alice should publish 3 NFTs', async () => { + // publish 3 nfts + nft1 = await nftFactory.createNFT(Alice, { + name: 'testNft1', + symbol: 'TSTF1', + templateIndex: 1, + tokenURI: '', + transferable: true, + owner: Alice + }) + nft2 = await nftFactory.createNFT(Alice, { + name: 'testNft2', + symbol: 'TSTF2', + templateIndex: 1, + tokenURI: '', + transferable: true, + owner: Alice + }) + nft3 = await nftFactory.createNFT(Alice, { + name: 'testNft3', + symbol: 'TSTF3', + templateIndex: 1, + tokenURI: '', + transferable: true, + owner: Alice + }) + }) + + it('Alice should allocate 10% to NFT1', async () => { + const totalAllocation = await veAllocate.getTotalAllocation(Alice) + const tx = await veAllocate.setAllocation(Alice, '1000', nft1, chainId) + assert(tx.events.AllocationSet.returnValues[0] === Alice) + assert(tx.events.AllocationSet.returnValues[1] === nft1) + assert(parseInt(tx.events.AllocationSet.returnValues[2]) === parseInt(chainId)) + assert(tx.events.AllocationSet.returnValues[3] === '1000') + const newTotalAllocation = await veAllocate.getTotalAllocation(Alice) + const expectedAllocation = parseInt(String(totalAllocation)) + 1000 + assert( + parseInt(String(newTotalAllocation)) === parseInt(String(expectedAllocation)), + 'NewAllocation (' + newTotalAllocation + ') should be ' + expectedAllocation + ) + const nftAllocation = await veAllocate.getVeAllocation(Alice, nft1, chainId) + assert( + parseInt(String(nftAllocation)) === parseInt('1000'), + nftAllocation + ' should be 1000' + ) + }) + + it('Alice should allocate 10% to NFT2 and 20% to NFT3', async () => { + const totalAllocation = await veAllocate.getTotalAllocation(Alice) + const tx = await veAllocate.setBatchAllocation( + Alice, + ['1000', '2000'], + [nft2, nft3], + [chainId, chainId] + ) + assert(tx.events.AllocationSetMultiple.returnValues[0] === Alice) + assert(tx.events.AllocationSetMultiple.returnValues[1][0] === nft2) + assert(tx.events.AllocationSetMultiple.returnValues[1][1] === nft3) + assert( + parseInt(tx.events.AllocationSetMultiple.returnValues[2][0]) === parseInt(chainId) + ) + assert(tx.events.AllocationSetMultiple.returnValues[3][0] === '1000') + assert(tx.events.AllocationSetMultiple.returnValues[3][1] === '2000') + const newTotalAllocation = await veAllocate.getTotalAllocation(Alice) + const expectedAllocation = parseInt(String(totalAllocation)) + 3000 + assert( + parseInt(String(newTotalAllocation)) === parseInt(String(expectedAllocation)), + 'NewAllocation (' + newTotalAllocation + ') should be ' + expectedAllocation + ) + let nftAllocation = await veAllocate.getVeAllocation(Alice, nft2, chainId) + assert( + parseInt(String(nftAllocation)) === parseInt('1000'), + nftAllocation + ' should be 1000' + ) + nftAllocation = await veAllocate.getVeAllocation(Alice, nft3, chainId) + assert( + parseInt(String(nftAllocation)) === parseInt('2000'), + nftAllocation + ' should be 2000' + ) + }) +})