From 44d97769832b8b6c001bbf89cbcce0c365a75db6 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 6 Feb 2024 16:21:25 +0100 Subject: [PATCH 1/2] update region price --- contracts/coretime_market/src/lib.rs | 35 ++++-- tests/market/list.test.ts | 2 +- tests/market/purchase.test.ts | 4 +- tests/market/unlist.test.ts | 2 + tests/market/updatePrice.test.ts | 174 +++++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 tests/market/updatePrice.test.ts diff --git a/contracts/coretime_market/src/lib.rs b/contracts/coretime_market/src/lib.rs index 752afb4..6e65db0 100755 --- a/contracts/coretime_market/src/lib.rs +++ b/contracts/coretime_market/src/lib.rs @@ -84,7 +84,7 @@ pub mod coretime_market { #[ink(event)] pub struct RegionUnlisted { - /// The identifier of the region that got listed on sale. + /// The identifier of the region that got unlisted from sale. #[ink(topic)] pub(crate) region_id: RawRegionId, /// The account that removed the region from sale. @@ -93,7 +93,7 @@ pub mod coretime_market { #[ink(event)] pub struct RegionPurchased { - /// The identifier of the region that got listed on sale. + /// The identifier of the region that got purchased. #[ink(topic)] pub(crate) region_id: RawRegionId, /// The buyer of the region @@ -102,6 +102,15 @@ pub mod coretime_market { pub(crate) total_price: Balance, } + #[ink(event)] + pub struct RegionPriceUpdated { + /// The identifier of the region that got its price updated. + #[ink(topic)] + pub(crate) region_id: RawRegionId, + /// The new per timeslice price. + pub(crate) new_timeslice_price: Balance, + } + impl CoretimeMarket { #[ink(constructor)] pub fn new( @@ -270,18 +279,30 @@ pub mod coretime_market { Ok(()) } - /// A function for updating a listed region's bit price. + /// A function for updating a listed region's price. /// /// ## Arguments: /// - `region_id`: The `u128` encoded identifier of the region being listed for sale. /// - `timeslice_price`: The new per timeslice price of the region. #[ink(message)] pub fn update_region_price( - &self, - _region_id: RawRegionId, - _new_timeslice_price: Balance, + &mut self, + id: Id, + new_timeslice_price: Balance, ) -> Result<(), MarketError> { - todo!() + let caller = self.env().caller(); + + let Id::U128(region_id) = id else { return Err(MarketError::InvalidRegionId) }; + + let mut listing = self.listings.get(®ion_id).ok_or(MarketError::RegionNotListed)?; + + ensure!(caller == listing.seller, MarketError::NotAllowed); + + listing.timeslice_price = new_timeslice_price; + self.listings.insert(®ion_id, &listing); + + self.emit_event(RegionPriceUpdated { region_id, new_timeslice_price }); + Ok(()) } /// A function for purchasing a region listed on sale. diff --git a/tests/market/list.test.ts b/tests/market/list.test.ts index 50c4379..f7fb3b1 100644 --- a/tests/market/list.test.ts +++ b/tests/market/list.test.ts @@ -93,7 +93,7 @@ describe('Coretime market listing', () => { }); await expectOnSale(market, id, alice, timeslicePrice); - expect((await market.query.regionPrice(id)).value.unwrap().ok.toNumber()).to.be.equal( + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( timeslicePrice * (region.getEnd() - region.getBegin()), ); expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.deep.equal(market.address); diff --git a/tests/market/purchase.test.ts b/tests/market/purchase.test.ts index b9866fe..e7df0e8 100644 --- a/tests/market/purchase.test.ts +++ b/tests/market/purchase.test.ts @@ -87,7 +87,7 @@ describe('Coretime market purchases', () => { .tx.listRegion(id, timeslicePrice, alice.address, { value: LISTING_DEPOIST }); await expectOnSale(market, id, alice, timeslicePrice); - expect((await market.query.regionPrice(id)).value.unwrap().ok.toNumber()).to.be.equal( + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( timeslicePrice * (region.getEnd() - region.getBegin()), ); expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.deep.equal(market.address); @@ -146,7 +146,7 @@ describe('Coretime market purchases', () => { .tx.listRegion(id, timeslicePrice, alice.address, { value: LISTING_DEPOIST }); await expectOnSale(market, id, alice, timeslicePrice); - expect((await market.query.regionPrice(id)).value.unwrap().ok.toNumber()).to.be.equal( + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( timeslicePrice * (region.getEnd() - region.getBegin()), ); expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.deep.equal(market.address); diff --git a/tests/market/unlist.test.ts b/tests/market/unlist.test.ts index 390a8e3..2258455 100644 --- a/tests/market/unlist.test.ts +++ b/tests/market/unlist.test.ts @@ -178,6 +178,7 @@ describe('Coretime market unlisting', () => { expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.be.equal(alice.address); }); + /* TODO: Come up with a better way to test this. it('Anyone can unlist an expired region', async () => { const regionId: RegionId = { begin: 0, @@ -225,4 +226,5 @@ describe('Coretime market unlisting', () => { // TODO: should ideally ensure that bob received the reward. }); + */ }); diff --git a/tests/market/updatePrice.test.ts b/tests/market/updatePrice.test.ts new file mode 100644 index 0000000..a4a3eb9 --- /dev/null +++ b/tests/market/updatePrice.test.ts @@ -0,0 +1,174 @@ +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { expect, use } from 'chai'; +import { KeyringPair } from '@polkadot/keyring/types'; +import XcRegions_Factory from '../../types/constructors/xc_regions'; +import Market_Factory from '../../types/constructors/coretime_market'; +import XcRegions from '../../types/contracts/xc_regions'; +import Market from '../../types/contracts/coretime_market'; +import chaiAsPromised from 'chai-as-promised'; +import { CoreMask, Id, Region, RegionId, RegionRecord } from 'coretime-utils'; +import { + approveTransfer, + balanceOf, + createRegionCollection, + expectEvent, + expectOnSale, + initRegion, + mintRegion, +} from '../common'; +import { MarketErrorBuilder } from '../../types/types-returns/coretime_market'; + +use(chaiAsPromised); + +const REGION_COLLECTION_ID = 42; +const LISTING_DEPOIST = 0; +// In reality this is 80, however we use 8 for testing. +const TIMESLICE_PERIOD = 8; + +const wsProvider = new WsProvider('ws://127.0.0.1:9944'); +// Create a keyring instance +const keyring = new Keyring({ type: 'sr25519', ss58Format: 5 }); + +describe('Coretime market purchases', () => { + let api: ApiPromise; + let alice: KeyringPair; + let bob: KeyringPair; + let charlie: KeyringPair; + + let xcRegions: XcRegions; + let market: Market; + + beforeEach(async function (): Promise { + api = await ApiPromise.create({ provider: wsProvider, noInitWarn: true, types: { Id } }); + + alice = keyring.addFromUri('//Alice'); + bob = keyring.addFromUri('//Bob'); + charlie = keyring.addFromUri('//Charlie'); + + const xcRegionsFactory = new XcRegions_Factory(api, alice); + xcRegions = new XcRegions((await xcRegionsFactory.new()).address, alice, api); + + const marketFactory = new Market_Factory(api, alice); + market = new Market( + (await marketFactory.new(xcRegions.address, LISTING_DEPOIST, TIMESLICE_PERIOD)).address, + alice, + api, + ); + + if (!(await api.query.uniques.class(REGION_COLLECTION_ID)).toHuman()) { + await createRegionCollection(api, alice); + } + }); + + it('Updating price works', async () => { + const regionId: RegionId = { + begin: 30, + core: 40, + mask: CoreMask.completeMask(), + }; + const regionRecord: RegionRecord = { + end: 60, + owner: alice.address, + paid: null, + }; + const region = new Region(regionId, regionRecord); + + await mintRegion(api, alice, region); + await approveTransfer(api, alice, region, xcRegions.address); + + await initRegion(api, xcRegions, alice, region); + + const id: any = api.createType('Id', { U128: region.getEncodedRegionId(api) }); + await xcRegions.withSigner(alice).tx.approve(market.address, id, true); + + const timeslicePrice = 5 * Math.pow(10, 12); + await market + .withSigner(alice) + .tx.listRegion(id, timeslicePrice, alice.address, { value: LISTING_DEPOIST }); + + await expectOnSale(market, id, alice, timeslicePrice); + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( + timeslicePrice * (region.getEnd() - region.getBegin()), + ); + expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.deep.equal(market.address); + + const newTimeslicePrice = 6 * Math.pow(10, 12); + + const result = await market.withSigner(alice).tx.updateRegionPrice(id, newTimeslicePrice); + expectEvent(result, 'RegionPriceUpdated', { + regionId: id.toPrimitive().u128, + newTimeslicePrice: newTimeslicePrice.toString(), + }); + await expectOnSale(market, id, alice, newTimeslicePrice); + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( + newTimeslicePrice * (region.getEnd() - region.getBegin()), + ); + }); + + it('Cannot update price for unlisted region', async () => { + const regionId: RegionId = { + begin: 30, + core: 41, + mask: CoreMask.completeMask(), + }; + const regionRecord: RegionRecord = { + end: 60, + owner: alice.address, + paid: null, + }; + const region = new Region(regionId, regionRecord); + + await mintRegion(api, alice, region); + await approveTransfer(api, alice, region, xcRegions.address); + + await initRegion(api, xcRegions, alice, region); + + const id: any = api.createType('Id', { U128: region.getEncodedRegionId(api) }); + await xcRegions.withSigner(alice).tx.approve(market.address, id, true); + + const newTimeslicePrice = 6 * Math.pow(10, 12); + + const result = await market.withSigner(alice).query.updateRegionPrice(id, newTimeslicePrice); + + expect(result.value.unwrap().err).to.deep.equal(MarketErrorBuilder.RegionNotListed()); + }); + + it('Only owner can update the price', async () => { + const regionId: RegionId = { + begin: 30, + core: 42, + mask: CoreMask.completeMask(), + }; + const regionRecord: RegionRecord = { + end: 60, + owner: alice.address, + paid: null, + }; + const region = new Region(regionId, regionRecord); + + await mintRegion(api, alice, region); + await approveTransfer(api, alice, region, xcRegions.address); + + await initRegion(api, xcRegions, alice, region); + + const id: any = api.createType('Id', { U128: region.getEncodedRegionId(api) }); + await xcRegions.withSigner(alice).tx.approve(market.address, id, true); + + const timeslicePrice = 7 * Math.pow(10, 12); + await market + .withSigner(alice) + .tx.listRegion(id, timeslicePrice, alice.address, { value: LISTING_DEPOIST }); + + await expectOnSale(market, id, alice, timeslicePrice); + expect((await market.query.regionPrice(id)).value.unwrap().unwrap().toNumber()).to.be.equal( + timeslicePrice * (region.getEnd() - region.getBegin()), + ); + expect((await xcRegions.query.ownerOf(id)).value.unwrap()).to.deep.equal(market.address); + + const newTimeslicePrice = 6 * Math.pow(10, 12); + + const result = await market.withSigner(bob).query.updateRegionPrice(id, newTimeslicePrice); + + expect(result.value.unwrap().err).to.deep.equal(MarketErrorBuilder.NotAllowed()); + }); +}); From 1566f38369ccce923456b9025db67c8824b53865 Mon Sep 17 00:00:00 2001 From: Szegoo Date: Tue, 6 Feb 2024 16:25:05 +0100 Subject: [PATCH 2/2] toolchain --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 465c14b..bbb9dfe 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly" +channel = "nightly-2023-12-22" components = [ "rustfmt", "clippy" ] targets = [ "wasm32-unknown-unknown"] profile = "minimal"