From 2aa2b28c34479a267f715100e329cfe19de2cf3f Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Wed, 29 May 2024 16:13:23 +0500 Subject: [PATCH 1/6] feat: Auction Contract Updates to Offer Up --- contract/,tx.json | 1 + contract/src/offer-up.contract.js | 80 ++++++++++++++++++++++++++----- contract/test/test-contract.js | 4 +- ui/src/App.tsx | 11 +++-- 4 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 contract/,tx.json diff --git a/contract/,tx.json b/contract/,tx.json new file mode 100644 index 0000000..bcc220d --- /dev/null +++ b/contract/,tx.json @@ -0,0 +1 @@ +{"height":"1041","txhash":"97AD126A4FBF7A4522275A12FD21EEF14FE2A502D0A2E489785CEAA56CA1F9A5","codespace":"","code":0,"data":"12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365","raw_log":"[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"coin_received","attributes":[{"key":"receiver","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"amount","value":"321000000ubld"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]},{"type":"message","attributes":[{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"module","value":"bank"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]}]}],"info":"","gas_wanted":"82400","gas_used":"65647","tx":null,"timestamp":"","events":[{"type":"tx","attributes":[{"key":"ZmVl","value":"","index":true},{"key":"ZmVlX3BheWVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRsLzYw","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"MW1MQS9GRTlsM09EMW9yUkN3LzdBbFdWTGNKRnorMjRWcldGenpkTUEvOUhrcEhYU1N6Q3JpQmtwazlDakFCd0pXVElGajBMTEtJUTFncUl1cU5kRVE9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"YmFuaw==","index":true}]}]} diff --git a/contract/src/offer-up.contract.js b/contract/src/offer-up.contract.js index e24f7d7..7ba35b2 100644 --- a/contract/src/offer-up.contract.js +++ b/contract/src/offer-up.contract.js @@ -21,9 +21,9 @@ import { Far } from '@endo/far'; import { M, getCopyBagEntries } from '@endo/patterns'; -import { AssetKind } from '@agoric/ertp/src/amountMath.js'; +import { AssetKind, AmountMath } from '@agoric/ertp/src/amountMath.js'; import { AmountShape } from '@agoric/ertp/src/typeGuards.js'; -import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; +import { atomicRearrange, atomicTransfer } from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; import '@agoric/zoe/exported.js'; const { Fail, quote: q } = assert; @@ -70,7 +70,7 @@ export const customTermsShape = meta.customTermsShape; * @param {ZCF} zcf */ export const start = async zcf => { - const { tradePrice, maxItems = 3n } = zcf.getTerms(); + const { tradePrice, maxItems = 1n } = zcf.getTerms(); /** * a new ERTP mint for items, accessed thru the Zoe Contract Facet. @@ -91,34 +91,88 @@ export const start = async zcf => { */ const proposalShape = harden({ give: { Price: M.gte(tradePrice) }, - want: { Items: { brand: itemBrand, value: M.bag() } }, + // want: { Items: { brand: itemBrand, value: M.bag() } }, + want: { }, exit: M.any(), }); /** a seat for allocating proceeds of sales */ const proceeds = zcf.makeEmptySeatKit().zcfSeat; + let bidsRegister = new Map(); + let currentNumOfBids = 0; + const maxBids = 3; + /** @type {OfferHandler} */ - const tradeHandler = buyerSeat => { + const tradeHandler = (buyerSeat, offerArgs) => { // give and want are guaranteed by Zoe to match proposalShape - const { want } = buyerSeat.getProposal(); + const { give } = buyerSeat.getProposal(); + const { want } = offerArgs; + // const { offerArgs } = buyerSeat.getOfferArgs(); sum(bagCounts(want.Items.value)) <= maxItems || - Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; + Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; - const newItems = itemMint.mintGains(want); + // const newItems = itemMint.mintGains(want); + atomicRearrange( zcf, harden([ // price from buyer to proceeds - [buyerSeat, proceeds, { Price: tradePrice }], - // new items to buyer - [newItems, buyerSeat, want], + [buyerSeat, proceeds, { Price: give.Price }], ]), ); - + ++currentNumOfBids; + const buyerKey = Date.now().toString(); + bidsRegister.set( buyerKey, {buyerSeat , bidValue: give.Price} ); + + + if (currentNumOfBids == maxBids) { + currentNumOfBids = 0; + // Find maximum bids - if there are multiple bids with maximum value, the first one is picked. + // let maxBidValue = {...give.Price }; + let maxBidValue = 0; + let buyerSeat; + + bidsRegister.forEach((value) => { + // We need to use Amount.isGTE instead + if ( value.bidValue.value > maxBidValue ) { + maxBidValue = value.bidValue.value; + buyerSeat = value.buyerSeat; + } + }); + + const newItems = itemMint.mintGains(want); + + atomicRearrange( + zcf, + harden([ + [newItems, buyerSeat, want], + ]), + ); + newItems.exit(); + + bidsRegister.forEach((value) => { + // We need to use Amount.isGTE instead + if (value.bidValue.value < maxBidValue) { + //maxBidValue = value.bidValue; + buyerSeat = value.buyerSeat; + atomicRearrange( + zcf, + harden([ + [proceeds, buyerSeat, value.bidValue ], + + ]), + ); + + } + }); + bidsRegister.clear(); + + } + buyerSeat.exit(true); - newItems.exit(); + return 'trade complete'; }; diff --git a/contract/test/test-contract.js b/contract/test/test-contract.js index 6dc1f6a..591f422 100644 --- a/contract/test/test-contract.js +++ b/contract/test/test-contract.js @@ -91,7 +91,7 @@ const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll']) => { const choiceBag = makeCopyBag(choices.map(name => [name, 1n])); const proposal = { give: { Price: tradePrice }, - want: { Items: AmountMath.make(brands.Item, choiceBag) }, + // want: { Items: AmountMath.make(brands.Item, choiceBag) }, }; const pmt = await E(purse).withdraw(tradePrice); t.log('Alice gives', proposal.give); @@ -105,7 +105,7 @@ const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll']) => { const actual = await E(issuers.Item).getAmountOf(items); t.log('Alice payout brand', actual.brand); t.log('Alice payout value', actual.value); - t.deepEqual(actual, proposal.want.Items); + //t.deepEqual(actual, proposal.want.Items); }; test('Alice trades: give some play money, want items', async t => { diff --git a/ui/src/App.tsx b/ui/src/App.tsx index d62c47a..b91ff16 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -70,6 +70,7 @@ const connectWallet = async () => { }; const makeOffer = (giveValue: bigint, wantChoices: Record) => { +//const makeOffer = (giveValue: bigint) => { const { wallet, offerUpInstance, brands } = useAppStore.getState(); if (!offerUpInstance) throw Error('no contract instance'); if (!(brands && brands.IST && brands.Item)) @@ -78,18 +79,20 @@ const makeOffer = (giveValue: bigint, wantChoices: Record) => { const value = makeCopyBag(entries(wantChoices)); const want = { Items: { brand: brands.Item, value } }; const give = { Price: { brand: brands.IST, value: giveValue } }; - + + wallet?.makeOffer( { source: 'contract', instance: offerUpInstance, publicInvitationMaker: 'makeTradeInvitation', }, - { give, want }, - undefined, + { give }, + // undefined, + { want }, (update: { status: string; data?: unknown }) => { if (update.status === 'error') { - alert(`Offer error: ${update.data}`); + alert(`Offer error: ${ JSON.stringify(update.data) }`); } if (update.status === 'accepted') { alert('Offer accepted'); From 328c1a6b58d0b49905204e29cffa3cabe6429fbf Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Thu, 30 May 2024 15:29:14 +0500 Subject: [PATCH 2/6] feat: second day of auction contract --- contract/,tx.json | 2 +- contract/src/offer-up.contract.js | 131 +++++++++++++++++++----------- contract/test/test-contract.js | 30 ++++++- ui/src/App.tsx | 2 +- 4 files changed, 112 insertions(+), 53 deletions(-) diff --git a/contract/,tx.json b/contract/,tx.json index bcc220d..3702f2e 100644 --- a/contract/,tx.json +++ b/contract/,tx.json @@ -1 +1 @@ -{"height":"1041","txhash":"97AD126A4FBF7A4522275A12FD21EEF14FE2A502D0A2E489785CEAA56CA1F9A5","codespace":"","code":0,"data":"12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365","raw_log":"[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"coin_received","attributes":[{"key":"receiver","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"amount","value":"321000000ubld"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]},{"type":"message","attributes":[{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"module","value":"bank"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]}]}],"info":"","gas_wanted":"82400","gas_used":"65647","tx":null,"timestamp":"","events":[{"type":"tx","attributes":[{"key":"ZmVl","value":"","index":true},{"key":"ZmVlX3BheWVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRsLzYw","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"MW1MQS9GRTlsM09EMW9yUkN3LzdBbFdWTGNKRnorMjRWcldGenpkTUEvOUhrcEhYU1N6Q3JpQmtwazlDakFCd0pXVElGajBMTEtJUTFncUl1cU5kRVE9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"YmFuaw==","index":true}]}]} +{"height":"1026","txhash":"97AD126A4FBF7A4522275A12FD21EEF14FE2A502D0A2E489785CEAA56CA1F9A5","codespace":"","code":0,"data":"12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365","raw_log":"[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"coin_received","attributes":[{"key":"receiver","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"amount","value":"321000000ubld"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]},{"type":"message","attributes":[{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"module","value":"bank"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]}]}],"info":"","gas_wanted":"82400","gas_used":"65647","tx":null,"timestamp":"","events":[{"type":"tx","attributes":[{"key":"ZmVl","value":"","index":true},{"key":"ZmVlX3BheWVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRsLzYw","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"MW1MQS9GRTlsM09EMW9yUkN3LzdBbFdWTGNKRnorMjRWcldGenpkTUEvOUhrcEhYU1N6Q3JpQmtwazlDakFCd0pXVElGajBMTEtJUTFncUl1cU5kRVE9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"YmFuaw==","index":true}]}]} diff --git a/contract/src/offer-up.contract.js b/contract/src/offer-up.contract.js index 7ba35b2..5620fc3 100644 --- a/contract/src/offer-up.contract.js +++ b/contract/src/offer-up.contract.js @@ -23,11 +23,22 @@ import { Far } from '@endo/far'; import { M, getCopyBagEntries } from '@endo/patterns'; import { AssetKind, AmountMath } from '@agoric/ertp/src/amountMath.js'; import { AmountShape } from '@agoric/ertp/src/typeGuards.js'; -import { atomicRearrange, atomicTransfer } from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; +import { + atomicRearrange, + atomicTransfer, +} from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; import '@agoric/zoe/exported.js'; +import { + withdrawFromSeat, + depositToSeat, +} from '@agoric/zoe/src/contractSupport/index.js'; const { Fail, quote: q } = assert; +// Added for debugging/console.log +const bigintReplacer = (key, value) => + typeof value === 'bigint' ? value.toString() : value; + // #region bag utilities /** @type { (xs: bigint[]) => bigint } */ const sum = xs => xs.reduce((acc, x) => acc + x, 0n); @@ -59,6 +70,32 @@ export const meta = { { maxItems: M.bigint() }, ), }; + +const isObject = object => { + return object != null && typeof object === 'object'; +}; +const isDeepEqual = (object1, object2) => { + const objKeys1 = Object.keys(object1); + const objKeys2 = Object.keys(object2); + + if (objKeys1.length !== objKeys2.length) return false; + + for (var key of objKeys1) { + const value1 = object1[key]; + const value2 = object2[key]; + + const isObjects = isObject(value1) && isObject(value2); + + if ( + (isObjects && !isDeepEqual(value1, value2)) || + (!isObjects && value1 !== value2) + ) { + return false; + } + } + return true; +}; + // compatibility with an earlier contract metadata API export const customTermsShape = meta.customTermsShape; @@ -92,7 +129,7 @@ export const start = async zcf => { const proposalShape = harden({ give: { Price: M.gte(tradePrice) }, // want: { Items: { brand: itemBrand, value: M.bag() } }, - want: { }, + want: {}, exit: M.any(), }); @@ -101,7 +138,6 @@ export const start = async zcf => { let bidsRegister = new Map(); let currentNumOfBids = 0; const maxBids = 3; - /** @type {OfferHandler} */ const tradeHandler = (buyerSeat, offerArgs) => { @@ -111,68 +147,67 @@ export const start = async zcf => { // const { offerArgs } = buyerSeat.getOfferArgs(); sum(bagCounts(want.Items.value)) <= maxItems || - Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; - - // const newItems = itemMint.mintGains(want); - - atomicRearrange( - zcf, - harden([ - // price from buyer to proceeds - [buyerSeat, proceeds, { Price: give.Price }], - ]), - ); + Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; + + // atomicRearrange( + // zcf, + // harden([ + // // price from buyer to proceeds + // [buyerSeat, proceeds, { Price: give.Price }], + // ]), + // ); + ++currentNumOfBids; - const buyerKey = Date.now().toString(); - bidsRegister.set( buyerKey, {buyerSeat , bidValue: give.Price} ); - + // const buyerKey = Date.now().toString(); + bidsRegister.set(currentNumOfBids.toString(), { + buyerSeat, + bidValue: give.Price, + }); + + console.log( + `New offer received: ${JSON.stringify( + give.Price, + bigintReplacer, + )} ${JSON.stringify(bidsRegister, bigintReplacer)}`, + ); + + // bidsRegister.forEach((value, key) => { + // console.log(`Bids Register holds following data values : ${value}`); + // }); if (currentNumOfBids == maxBids) { + currentNumOfBids = 0; // Find maximum bids - if there are multiple bids with maximum value, the first one is picked. - // let maxBidValue = {...give.Price }; - let maxBidValue = 0; + let maxBidValue = harden({ ...give.Price }); + // let maxBidValue = give.Price; let buyerSeat; - - bidsRegister.forEach((value) => { + bidsRegister.forEach(value => { // We need to use Amount.isGTE instead - if ( value.bidValue.value > maxBidValue ) { - maxBidValue = value.bidValue.value; + if (AmountMath.isGTE(value.bidValue, maxBidValue)) { + maxBidValue = value.bidValue; buyerSeat = value.buyerSeat; } }); - const newItems = itemMint.mintGains(want); - - atomicRearrange( - zcf, - harden([ - [newItems, buyerSeat, want], - ]), - ); + atomicRearrange(zcf, harden([ + [newItems, buyerSeat, want], + [buyerSeat, proceeds, {Price: maxBidValue}] + ])); newItems.exit(); - - bidsRegister.forEach((value) => { - // We need to use Amount.isGTE instead - if (value.bidValue.value < maxBidValue) { - //maxBidValue = value.bidValue; - buyerSeat = value.buyerSeat; - atomicRearrange( - zcf, - harden([ - [proceeds, buyerSeat, value.bidValue ], - - ]), - ); + buyerSeat.exit(true); + bidsRegister.forEach(async value => { + if ( !value.buyerSeat.hasExited() ) { + + value.buyerSeat.exit(true); } }); bidsRegister.clear(); - } - - buyerSeat.exit(true); - + + // buyerSeat.exit(true); + return 'trade complete'; }; diff --git a/contract/test/test-contract.js b/contract/test/test-contract.js index 591f422..cb3d412 100644 --- a/contract/test/test-contract.js +++ b/contract/test/test-contract.js @@ -102,9 +102,9 @@ const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll']) => { const seat = E(zoe).offer(toTrade, proposal, { Price: pmt }); const items = await E(seat).getPayout('Items'); - const actual = await E(issuers.Item).getAmountOf(items); - t.log('Alice payout brand', actual.brand); - t.log('Alice payout value', actual.value); + // const actual = await E(issuers.Item).getAmountOf(items); + // t.log('Alice payout brand', actual.brand); + // t.log('Alice payout value', actual.value); //t.deepEqual(actual, proposal.want.Items); }; @@ -227,3 +227,27 @@ test('use the code that will go on chain to start the contract', async t => { const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); await alice(t, zoe, instance, await faucet(5n * UNIT6)); }); + + +test('buySeats saved in Maps', async t => { + + const startContract = async ({ zoe, bundle }) => { + /** @type {ERef>} */ + const installation = E(zoe).install(bundle); + const feeIssuer = await E(zoe).getFeeIssuer(); + const feeBrand = await E(feeIssuer).getBrand(); + const tradePrice = AmountMath.make(feeBrand, 25n * CENT); + return E(zoe).startInstance( + installation, + { Price: feeIssuer }, + { tradePrice }, + ); + }; + + const { zoe, bundle, bundleCache, feeMintAccess } = t.context; + const { instance } = await startContract({ zoe, bundle }); + const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); + await alice(t, zoe, instance, await faucet(5n * UNIT6)); + await alice(t, zoe, instance, await faucet(5n * UNIT6)); + await alice(t, zoe, instance, await faucet(5n * UNIT6)); +}); diff --git a/ui/src/App.tsx b/ui/src/App.tsx index b91ff16..71fb9b7 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -87,7 +87,7 @@ const makeOffer = (giveValue: bigint, wantChoices: Record) => { instance: offerUpInstance, publicInvitationMaker: 'makeTradeInvitation', }, - { give }, + { give}, // undefined, { want }, (update: { status: string; data?: unknown }) => { From e2e3c94d8bed77a47cd071b13f952811c43ff8d9 Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Fri, 31 May 2024 12:21:43 +0500 Subject: [PATCH 3/6] checkpoint1: --- contract/,tx.json | 2 +- contract/src/offer-up.contract.js | 40 +++++------------ contract/test/test-contract.js | 74 ++++++++++++++++++++++++++----- ui/src/App.tsx | 6 +-- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/contract/,tx.json b/contract/,tx.json index 3702f2e..8b94221 100644 --- a/contract/,tx.json +++ b/contract/,tx.json @@ -1 +1 @@ -{"height":"1026","txhash":"97AD126A4FBF7A4522275A12FD21EEF14FE2A502D0A2E489785CEAA56CA1F9A5","codespace":"","code":0,"data":"12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365","raw_log":"[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"coin_received","attributes":[{"key":"receiver","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"amount","value":"321000000ubld"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]},{"type":"message","attributes":[{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"module","value":"bank"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]}]}],"info":"","gas_wanted":"82400","gas_used":"65647","tx":null,"timestamp":"","events":[{"type":"tx","attributes":[{"key":"ZmVl","value":"","index":true},{"key":"ZmVlX3BheWVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRsLzYw","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"MW1MQS9GRTlsM09EMW9yUkN3LzdBbFdWTGNKRnorMjRWcldGenpkTUEvOUhrcEhYU1N6Q3JpQmtwazlDakFCd0pXVElGajBMTEtJUTFncUl1cU5kRVE9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"YmFuaw==","index":true}]}]} +{"height":"1085","txhash":"97AD126A4FBF7A4522275A12FD21EEF14FE2A502D0A2E489785CEAA56CA1F9A5","codespace":"","code":0,"data":"12260A242F636F736D6F732E62616E6B2E763162657461312E4D736753656E64526573706F6E7365","raw_log":"[{\"msg_index\":0,\"events\":[{\"type\":\"coin_received\",\"attributes\":[{\"key\":\"receiver\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"coin_spent\",\"attributes\":[{\"key\":\"spender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"/cosmos.bank.v1beta1.MsgSend\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"module\",\"value\":\"bank\"}]},{\"type\":\"transfer\",\"attributes\":[{\"key\":\"recipient\",\"value\":\"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk\"},{\"key\":\"sender\",\"value\":\"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl\"},{\"key\":\"amount\",\"value\":\"321000000ubld\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"coin_received","attributes":[{"key":"receiver","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"amount","value":"321000000ubld"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]},{"type":"message","attributes":[{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"module","value":"bank"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"agoric1rwwley550k9mmk6uq6mm6z4udrg8kyuyvfszjk"},{"key":"sender","value":"agoric1estsewt6jqsx77pwcxkn5ah0jqgu8rhgflwfdl"},{"key":"amount","value":"321000000ubld"}]}]}],"info":"","gas_wanted":"82400","gas_used":"65647","tx":null,"timestamp":"","events":[{"type":"tx","attributes":[{"key":"ZmVl","value":"","index":true},{"key":"ZmVlX3BheWVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"tx","attributes":[{"key":"YWNjX3NlcQ==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRsLzYw","index":true}]},{"type":"tx","attributes":[{"key":"c2lnbmF0dXJl","value":"MW1MQS9GRTlsM09EMW9yUkN3LzdBbFdWTGNKRnorMjRWcldGenpkTUEvOUhrcEhYU1N6Q3JpQmtwazlDakFCd0pXVElGajBMTEtJUTFncUl1cU5kRVE9PQ==","index":true}]},{"type":"message","attributes":[{"key":"YWN0aW9u","value":"L2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZA==","index":true}]},{"type":"coin_spent","attributes":[{"key":"c3BlbmRlcg==","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"coin_received","attributes":[{"key":"cmVjZWl2ZXI=","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"transfer","attributes":[{"key":"cmVjaXBpZW50","value":"YWdvcmljMXJ3d2xleTU1MGs5bW1rNnVxNm1tNno0dWRyZzhreXV5dmZzempr","index":true},{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true},{"key":"YW1vdW50","value":"MzIxMDAwMDAwdWJsZA==","index":true}]},{"type":"message","attributes":[{"key":"c2VuZGVy","value":"YWdvcmljMWVzdHNld3Q2anFzeDc3cHdjeGtuNWFoMGpxZ3U4cmhnZmx3ZmRs","index":true}]},{"type":"message","attributes":[{"key":"bW9kdWxl","value":"YmFuaw==","index":true}]}]} diff --git a/contract/src/offer-up.contract.js b/contract/src/offer-up.contract.js index 5620fc3..c429634 100644 --- a/contract/src/offer-up.contract.js +++ b/contract/src/offer-up.contract.js @@ -28,10 +28,6 @@ import { atomicTransfer, } from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; import '@agoric/zoe/exported.js'; -import { - withdrawFromSeat, - depositToSeat, -} from '@agoric/zoe/src/contractSupport/index.js'; const { Fail, quote: q } = assert; @@ -74,6 +70,7 @@ export const meta = { const isObject = object => { return object != null && typeof object === 'object'; }; + const isDeepEqual = (object1, object2) => { const objKeys1 = Object.keys(object1); const objKeys2 = Object.keys(object2); @@ -128,37 +125,30 @@ export const start = async zcf => { */ const proposalShape = harden({ give: { Price: M.gte(tradePrice) }, - // want: { Items: { brand: itemBrand, value: M.bag() } }, - want: {}, + want: { Items: { brand: itemBrand, value: M.bag() } }, +// want: {}, exit: M.any(), }); /** a seat for allocating proceeds of sales */ const proceeds = zcf.makeEmptySeatKit().zcfSeat; let bidsRegister = new Map(); + //TODO: Instead use the size of bids array let currentNumOfBids = 0; const maxBids = 3; /** @type {OfferHandler} */ - const tradeHandler = (buyerSeat, offerArgs) => { + const tradeHandler = (buyerSeat) => { // give and want are guaranteed by Zoe to match proposalShape - const { give } = buyerSeat.getProposal(); - const { want } = offerArgs; + // const { want } = offerArgs; + const { give, want } = buyerSeat.getProposal(); + // const { offerArgs } = buyerSeat.getOfferArgs(); sum(bagCounts(want.Items.value)) <= maxItems || Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; - // atomicRearrange( - // zcf, - // harden([ - // // price from buyer to proceeds - // [buyerSeat, proceeds, { Price: give.Price }], - // ]), - // ); - ++currentNumOfBids; - // const buyerKey = Date.now().toString(); bidsRegister.set(currentNumOfBids.toString(), { buyerSeat, bidValue: give.Price, @@ -170,11 +160,8 @@ export const start = async zcf => { bigintReplacer, )} ${JSON.stringify(bidsRegister, bigintReplacer)}`, ); - - // bidsRegister.forEach((value, key) => { - // console.log(`Bids Register holds following data values : ${value}`); - // }); - + console.log("\n\n\n The number of bids currently available is :", bidsRegister.size); + if (currentNumOfBids == maxBids) { currentNumOfBids = 0; @@ -199,16 +186,13 @@ export const start = async zcf => { bidsRegister.forEach(async value => { if ( !value.buyerSeat.hasExited() ) { - value.buyerSeat.exit(true); } }); bidsRegister.clear(); } - - // buyerSeat.exit(true); - - return 'trade complete'; + + return 'bid placed.'; }; /** diff --git a/contract/test/test-contract.js b/contract/test/test-contract.js index cb3d412..4c40e2f 100644 --- a/contract/test/test-contract.js +++ b/contract/test/test-contract.js @@ -16,6 +16,7 @@ import { AmountMath, makeIssuerKit } from '@agoric/ertp'; import { makeStableFaucet } from './mintStable.js'; import { startOfferUpContract } from '../src/offer-up-proposal.js'; +import { verify } from 'crypto'; /** @typedef {typeof import('../src/offer-up.contract.js').start} AssetContractFn */ @@ -82,30 +83,74 @@ test('Start the contract', async t => { * @param {Purse} purse * @param {string[]} choices */ -const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll']) => { +const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll'], bidValue = 25n * CENT, isMaxbid = false) => { const publicFacet = E(zoe).getPublicFacet(instance); // @ts-expect-error Promise seems to work const terms = await E(zoe).getTerms(instance); const { issuers, brands, tradePrice } = terms; + const bidPrice = AmountMath.make(tradePrice.brand, bidValue); + const zeroPrice = AmountMath.make(tradePrice.brand, 0n); const choiceBag = makeCopyBag(choices.map(name => [name, 1n])); const proposal = { - give: { Price: tradePrice }, - // want: { Items: AmountMath.make(brands.Item, choiceBag) }, + give: { Price: bidPrice }, + want: { Items: AmountMath.make(brands.Item, choiceBag) }, }; - const pmt = await E(purse).withdraw(tradePrice); + const pmt = await E(purse).withdraw(bidPrice); t.log('Alice gives', proposal.give); // #endregion makeProposal const toTrade = E(publicFacet).makeTradeInvitation(); - const seat = E(zoe).offer(toTrade, proposal, { Price: pmt }); - const items = await E(seat).getPayout('Items'); + const seat = await E(zoe).offer(toTrade, proposal, { Price: pmt } ); + return {seat, proposal, pmt}; + // const items = await E(seat).getPayout('Items') ; // const actual = await E(issuers.Item).getAmountOf(items); + + // t.log('Alice payout brand', actual.brand); + // t.log('Alice payout value', actual.value); + // if (isMaxbid){ + // t.deepEqual(actual, proposal.want.Items); + // const pmtAmount = await E(purse).deposit(pmt); + // t.deepEqual(zeroPrice, pmtAmount); + // } else { + // t.notDeepEqual(actual, proposal.want.Items); + // const pmtAmount = await E(purse).deposit(pmt); + // t.notDeepEqual(zeroPrice, pmtAmount); + // } + +}; +const verifyBidSeat = async (t, seat, proposal, pmt, zoe, instance, purse, isMaxbid = false) => { + + const terms = await E(zoe).getTerms(instance); + const { issuers, brands, tradePrice } = terms; + // const bidPrice = AmountMath.make(tradePrice.brand, bidValue); + const zeroPrice = AmountMath.make(tradePrice.brand, 0n); + + // t.log('Alice payout brand', actual.brand); // t.log('Alice payout value', actual.value); - //t.deepEqual(actual, proposal.want.Items); + if (isMaxbid){ + if ( seat.hasExited() ){ + console.log("User Seat has exited."); + } + const items = await E(seat).getPayout('Items') ; + const actual = await E(issuers.Item).getAmountOf(items); + t.deepEqual(actual, proposal.want.Items); + const pmtAmount = await E(purse).deposit(pmt); + t.deepEqual(zeroPrice, pmtAmount); + } else { + // t.notDeepEqual(actual, proposal.want.Items); + if (seat.hasExited()){ + console.log("User Seat has exited."); + } + const pmt = await E(seat).getPayout('IST') ; + const pmtAmount = await E(purse).deposit(pmt); + t.notDeepEqual(zeroPrice, pmtAmount); + } + + }; test('Alice trades: give some play money, want items', async t => { @@ -229,7 +274,7 @@ test('use the code that will go on chain to start the contract', async t => { }); -test('buySeats saved in Maps', async t => { +test('bidSeats saved in Maps', async t => { const startContract = async ({ zoe, bundle }) => { /** @type {ERef>} */ @@ -243,11 +288,16 @@ test('buySeats saved in Maps', async t => { { tradePrice }, ); }; - const { zoe, bundle, bundleCache, feeMintAccess } = t.context; const { instance } = await startContract({ zoe, bundle }); const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); - await alice(t, zoe, instance, await faucet(5n * UNIT6)); - await alice(t, zoe, instance, await faucet(5n * UNIT6)); - await alice(t, zoe, instance, await faucet(5n * UNIT6)); + const purse1 = await faucet(500n * UNIT6); + const purse2 = await faucet(500n * UNIT6); + const purse3 = await faucet(500n * UNIT6); + const {seat: seat1, proposal: proposal1, pmt: pmt1} = await alice(t, zoe, instance, purse1 , ['map'], 8n* UNIT6); + const {seat: seat2, proposal:proposal2, pmt: pmt2} = await alice(t, zoe, instance, purse2, ['map'], 7n* UNIT6, false); + const {seat: seat3, proposal:proposal3, pmt: pmt3} = await alice(t, zoe, instance, purse3, ['map'], 6n* UNIT6); + await verifyBidSeat(t, seat1, proposal1, pmt1, zoe, instance, purse1,true); + await verifyBidSeat(t, seat2, proposal2, pmt2, zoe, instance, purse2,false); + await verifyBidSeat(t, seat3, proposal3, pmt3, zoe, instance, purse3,false); }); diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 71fb9b7..c60a560 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -87,9 +87,9 @@ const makeOffer = (giveValue: bigint, wantChoices: Record) => { instance: offerUpInstance, publicInvitationMaker: 'makeTradeInvitation', }, - { give}, - // undefined, - { want }, + { give, want}, + undefined, + // { want }, (update: { status: string; data?: unknown }) => { if (update.status === 'error') { alert(`Offer error: ${ JSON.stringify(update.data) }`); From e76b13b90a36e54e5527c2c25508c3ac4a563c6e Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Fri, 31 May 2024 14:18:41 +0500 Subject: [PATCH 4/6] feat: updated test cases --- contract/test/test-contract.js | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/contract/test/test-contract.js b/contract/test/test-contract.js index 4c40e2f..99ddb43 100644 --- a/contract/test/test-contract.js +++ b/contract/test/test-contract.js @@ -121,36 +121,26 @@ const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll'], bidVa // } }; -const verifyBidSeat = async (t, seat, proposal, pmt, zoe, instance, purse, isMaxbid = false) => { +const verifyBidSeat = async (t, seat, proposal, zoe, instance, purse, isMaxbid = false) => { const terms = await E(zoe).getTerms(instance); const { issuers, brands, tradePrice } = terms; - // const bidPrice = AmountMath.make(tradePrice.brand, bidValue); const zeroPrice = AmountMath.make(tradePrice.brand, 0n); - // t.log('Alice payout brand', actual.brand); - // t.log('Alice payout value', actual.value); if (isMaxbid){ - if ( seat.hasExited() ){ - console.log("User Seat has exited."); - } const items = await E(seat).getPayout('Items') ; const actual = await E(issuers.Item).getAmountOf(items); t.deepEqual(actual, proposal.want.Items); + const pmt = await E(seat).getPayout('Price') ; const pmtAmount = await E(purse).deposit(pmt); t.deepEqual(zeroPrice, pmtAmount); } else { - // t.notDeepEqual(actual, proposal.want.Items); - if (seat.hasExited()){ - console.log("User Seat has exited."); - } - const pmt = await E(seat).getPayout('IST') ; + const pmt = await E(seat).getPayout('Price') ; const pmtAmount = await E(purse).deposit(pmt); t.notDeepEqual(zeroPrice, pmtAmount); } - }; test('Alice trades: give some play money, want items', async t => { @@ -294,10 +284,10 @@ test('bidSeats saved in Maps', async t => { const purse1 = await faucet(500n * UNIT6); const purse2 = await faucet(500n * UNIT6); const purse3 = await faucet(500n * UNIT6); - const {seat: seat1, proposal: proposal1, pmt: pmt1} = await alice(t, zoe, instance, purse1 , ['map'], 8n* UNIT6); - const {seat: seat2, proposal:proposal2, pmt: pmt2} = await alice(t, zoe, instance, purse2, ['map'], 7n* UNIT6, false); - const {seat: seat3, proposal:proposal3, pmt: pmt3} = await alice(t, zoe, instance, purse3, ['map'], 6n* UNIT6); - await verifyBidSeat(t, seat1, proposal1, pmt1, zoe, instance, purse1,true); - await verifyBidSeat(t, seat2, proposal2, pmt2, zoe, instance, purse2,false); - await verifyBidSeat(t, seat3, proposal3, pmt3, zoe, instance, purse3,false); + const {seat: seat1, proposal: proposal1} = await alice(t, zoe, instance, purse1 , ['map'], 8n* UNIT6); + const {seat: seat2, proposal:proposal2} = await alice(t, zoe, instance, purse2, ['map'], 7n* UNIT6, false); + const {seat: seat3, proposal:proposal3} = await alice(t, zoe, instance, purse3, ['map'], 6n* UNIT6); + await verifyBidSeat(t, seat1, proposal1, zoe, instance, purse1,true); + await verifyBidSeat(t, seat2, proposal2, zoe, instance, purse2,false); + await verifyBidSeat(t, seat3, proposal3, zoe, instance, purse3,false); }); From eb7d8d89a751da870a7bba542f0eb28d8b81dd4c Mon Sep 17 00:00:00 2001 From: rabi-siddique Date: Fri, 31 May 2024 14:48:53 +0500 Subject: [PATCH 5/6] chore: UI changes for displaying bidding info --- ui/src/App.tsx | 2 +- ui/src/components/Trade.tsx | 75 ++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/ui/src/App.tsx b/ui/src/App.tsx index c60a560..fdaf5af 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -131,7 +131,7 @@ function App() { return ( <> -

Items Listed on Offer Up

+

Items Listed for Auction

; inputClassName: string; inputStep?: string; -}) => ( -
- - {icon && } - {coinIcon && } - -
-); + option: boolean; +}) => { + let inputField; + if (option) { + inputField = ( + + ); + } else { + inputField = ( + + ); + } + return ( +
+ + {icon && } + {coinIcon && } + {inputField} +
+ ); +}; type TradeProps = { makeOffer: (giveValue: bigint, wantChoices: Record) => void; @@ -71,23 +92,21 @@ type TradeProps = { // TODO: IST displayInfo is available in vbankAsset or boardAux const Trade = ({ makeOffer, istPurse, walletConnected }: TradeProps) => { const [giveValue, setGiveValue] = useState(terms.price); - const [choices, setChoices] = useState({ map: 1n, scroll: 2n }); + const [choices, setChoices] = useState({ map: 1n }); const changeChoice = (ev: FormEvent) => { if (!ev.target) return; const elt = ev.target as HTMLInputElement; const title = elt.title as ItemName; if (!title) return; - const qty = BigInt(elt.value); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { [title]: _old, ...rest }: ItemChoices = choices; - const newChoices = qty > 0 ? { ...rest, [title]: qty } : rest; + const qty = BigInt(1); + const newChoices = { [title]: qty }; setChoices(newChoices); }; return ( <>
-

Want: Choose up to 3 items

+

Want: Choose 1 item to bid on

{entries(nameToIcon).map(([title, icon]) => ( { inputClassName={ sum(values(choices)) <= terms.maxItems ? 'ok' : 'error' } + option={true} /> ))}
-

Give: Offer at least 0.25 IST

+

Give: Bid at least 0.25 IST

{ } inputClassName={giveValue >= terms.price ? 'ok' : 'error'} inputStep="0.01" + option={false} />
{walletConnected && ( )}
From 5d8faace45998ef3e1b541234b8238642395670d Mon Sep 17 00:00:00 2001 From: Mudassir Shabbir Date: Fri, 31 May 2024 17:00:18 +0500 Subject: [PATCH 6/6] fix: finishing touches to auctions contract --- contract/src/offer-up-proposal.js | 2 +- contract/src/offer-up.contract.js | 199 +++++++++++------------------- contract/test/test-contract.js | 123 ++++++++---------- 3 files changed, 121 insertions(+), 203 deletions(-) diff --git a/contract/src/offer-up-proposal.js b/contract/src/offer-up-proposal.js index d35e1e3..ca21080 100644 --- a/contract/src/offer-up-proposal.js +++ b/contract/src/offer-up-proposal.js @@ -67,7 +67,7 @@ export const startOfferUpContract = async permittedPowers => { const istIssuer = await istIssuerP; const istBrand = await istBrandP; - const terms = { tradePrice: AmountMath.make(istBrand, 25n * CENT) }; + const terms = { minBidPrice: AmountMath.make(istBrand, 25n * CENT), maxBids: 3n }; // agoricNames gets updated each time; the promise space only once XXXXXXX const installation = await offerUpInstallationP; diff --git a/contract/src/offer-up.contract.js b/contract/src/offer-up.contract.js index c429634..dfd635e 100644 --- a/contract/src/offer-up.contract.js +++ b/contract/src/offer-up.contract.js @@ -11,7 +11,7 @@ * to make an invitation. * 3. client makes an offer using the invitation, along with * a proposal (with give and want) and payments. Zoe escrows the payments, and then - * 4. Zoe invokes the offer handler specified in step 2 -- here {@link tradeHandler}. + * 4. Zoe invokes the offer handler specified in step 2 -- here {@link bidHandler}. * * @see {@link https://docs.agoric.com/guides/zoe/|Zoe Overview} for a walk-thru of this contract * @see {@link https://docs.agoric.com/guides/js-programming/hardened-js.html|Hardened JavaScript} @@ -23,76 +23,33 @@ import { Far } from '@endo/far'; import { M, getCopyBagEntries } from '@endo/patterns'; import { AssetKind, AmountMath } from '@agoric/ertp/src/amountMath.js'; import { AmountShape } from '@agoric/ertp/src/typeGuards.js'; -import { - atomicRearrange, - atomicTransfer, -} from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; +import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js'; import '@agoric/zoe/exported.js'; -const { Fail, quote: q } = assert; - // Added for debugging/console.log -const bigintReplacer = (key, value) => +const bigintReplacer = (_, value) => typeof value === 'bigint' ? value.toString() : value; -// #region bag utilities -/** @type { (xs: bigint[]) => bigint } */ -const sum = xs => xs.reduce((acc, x) => acc + x, 0n); - -/** - * @param {import('@endo/patterns').CopyBag} bag - * @returns {bigint[]} - */ -const bagCounts = bag => { - const entries = getCopyBagEntries(bag); - return entries.map(([_k, ct]) => ct); -}; // #endregion /** * In addition to the standard `issuers` and `brands` terms, - * this contract is parameterized by terms for price and, - * optionally, a maximum number of items sold for that price (default: 3). + * this contract is parameterized by terms for bid value and, + * optionally, a maximum number of bids before closing an auction (default: 3). * * @typedef {{ - * tradePrice: Amount; - * maxItems?: bigint; - * }} OfferUpTerms + * minBidPrice: Amount; + * maxBids?: bigint; + * }} AuctionTerms */ export const meta = { customTermsShape: M.splitRecord( - { tradePrice: AmountShape }, + { minBidPrice: AmountShape }, { maxItems: M.bigint() }, ), }; -const isObject = object => { - return object != null && typeof object === 'object'; -}; - -const isDeepEqual = (object1, object2) => { - const objKeys1 = Object.keys(object1); - const objKeys2 = Object.keys(object2); - - if (objKeys1.length !== objKeys2.length) return false; - - for (var key of objKeys1) { - const value1 = object1[key]; - const value2 = object2[key]; - - const isObjects = isObject(value1) && isObject(value2); - - if ( - (isObjects && !isDeepEqual(value1, value2)) || - (!isObjects && value1 !== value2) - ) { - return false; - } - } - return true; -}; - // compatibility with an earlier contract metadata API export const customTermsShape = meta.customTermsShape; @@ -101,109 +58,93 @@ export const customTermsShape = meta.customTermsShape; * - creates a new non-fungible asset type for Items, and * - handles offers to buy up to `maxItems` items at a time. * - * @param {ZCF} zcf + * @param {ZCF} zcf */ export const start = async zcf => { - const { tradePrice, maxItems = 1n } = zcf.getTerms(); - - /** - * a new ERTP mint for items, accessed thru the Zoe Contract Facet. - * Note: `makeZCFMint` makes the associated brand and issuer available - * in the contract's terms. - * - * AssetKind.COPY_BAG can express non-fungible (or rather: semi-fungible) - * amounts such as: 3 potions and 1 map. - */ + const { minBidPrice, maxBids = 3n } = zcf.getTerms(); + const itemMint = await zcf.makeZCFMint('Item', AssetKind.COPY_BAG); const { brand: itemBrand } = itemMint.getIssuerRecord(); - /** - * a pattern to constrain proposals given to {@link tradeHandler} - * - * The `Price` amount must be >= `tradePrice` term. - * The `Items` amount must use the `Item` brand and a bag value. - */ const proposalShape = harden({ - give: { Price: M.gte(tradePrice) }, + give: { Price: M.gte(minBidPrice) }, want: { Items: { brand: itemBrand, value: M.bag() } }, -// want: {}, exit: M.any(), }); - /** a seat for allocating proceeds of sales */ + /** a seat for allocating proceeds of auction sales */ const proceeds = zcf.makeEmptySeatKit().zcfSeat; + // A register to keep all the bids of all items let bidsRegister = new Map(); - //TODO: Instead use the size of bids array - let currentNumOfBids = 0; - const maxBids = 3; /** @type {OfferHandler} */ - const tradeHandler = (buyerSeat) => { + const bidHandler = bidderSeat => { // give and want are guaranteed by Zoe to match proposalShape - // const { want } = offerArgs; - const { give, want } = buyerSeat.getProposal(); - - // const { offerArgs } = buyerSeat.getOfferArgs(); - - sum(bagCounts(want.Items.value)) <= maxItems || - Fail`max ${q(maxItems)} items allowed: ${q(want.Items)}`; - - ++currentNumOfBids; - bidsRegister.set(currentNumOfBids.toString(), { - buyerSeat, - bidValue: give.Price, - }); - - console.log( - `New offer received: ${JSON.stringify( - give.Price, - bigintReplacer, - )} ${JSON.stringify(bidsRegister, bigintReplacer)}`, - ); - console.log("\n\n\n The number of bids currently available is :", bidsRegister.size); - - if (currentNumOfBids == maxBids) { - - currentNumOfBids = 0; - // Find maximum bids - if there are multiple bids with maximum value, the first one is picked. - let maxBidValue = harden({ ...give.Price }); - // let maxBidValue = give.Price; - let buyerSeat; - bidsRegister.forEach(value => { - // We need to use Amount.isGTE instead - if (AmountMath.isGTE(value.bidValue, maxBidValue)) { - maxBidValue = value.bidValue; - buyerSeat = value.buyerSeat; + const { want } = bidderSeat.getProposal(); + const bidItem = JSON.stringify(want.Items.value, bigintReplacer); + console.log(' bidItem is : ', bidItem); + + /** + From the bidsRegister Map object, get the array of bids for the given item if it exists otherwise create one. + and then place the current bid in that array. + then count the number of bids in that array and check whether it is equal to the maxBids. + If the number of bids are equal to max bid, we want to find out value of the max bid in the array. + */ + + // Check if the item already has a bid array in the Map; if not, create one + if (!bidsRegister.has(bidItem)) { + bidsRegister.set(bidItem, []); + } + + // Get the existing bid array for the item + let bids = bidsRegister.get(bidItem); + + // Add the current bid to the bid array + bids.push(bidderSeat); + + // Check if the number of bids is equal to maxBids + if (bids.length == maxBids) { + console.log('Maximum bids reached for this item.'); + + // Find the maximum bid in the array + // Initialize the maximum bid as the first bid in the array + let maxBid = bids[0]; + // Loop through the bids array to find the maximum bid + for (let i = 1; i < bids.length; i++) { + if ( + AmountMath.isGTE( + bids[i].getCurrentAllocation().Price, + maxBid.getCurrentAllocation().Price, + ) + ) { + maxBid = bids[i]; } - }); + } + const newItems = itemMint.mintGains(want); - atomicRearrange(zcf, harden([ - [newItems, buyerSeat, want], - [buyerSeat, proceeds, {Price: maxBidValue}] - ])); + atomicRearrange( + zcf, + harden([ + [newItems, maxBid, want], + [maxBid, proceeds, { Price: maxBid.getCurrentAllocation().Price }], + ]), + ); newItems.exit(); - buyerSeat.exit(true); + maxBid.exit(true); - bidsRegister.forEach(async value => { - if ( !value.buyerSeat.hasExited() ) { - value.buyerSeat.exit(true); + bids.forEach(async bidderSeatItem => { + console.log(JSON.stringify(bidderSeatItem)); + if (!bidderSeatItem.hasExited()) { + bidderSeatItem.exit(true); } }); - bidsRegister.clear(); + bidsRegister.set(bidItem, []); } - return 'bid placed.'; }; - /** - * Make an invitation to trade for items. - * - * Proposal Keywords used in offers using these invitations: - * - give: `Price` - * - want: `Items` - */ const makeTradeInvitation = () => - zcf.makeInvitation(tradeHandler, 'buy items', undefined, proposalShape); + zcf.makeInvitation(bidHandler, 'bid on items', undefined, proposalShape); // Mark the publicFacet Far, i.e. reachable from outside the contract const publicFacet = Far('Items Public Facet', { diff --git a/contract/test/test-contract.js b/contract/test/test-contract.js index 99ddb43..11c5685 100644 --- a/contract/test/test-contract.js +++ b/contract/test/test-contract.js @@ -16,7 +16,7 @@ import { AmountMath, makeIssuerKit } from '@agoric/ertp'; import { makeStableFaucet } from './mintStable.js'; import { startOfferUpContract } from '../src/offer-up-proposal.js'; -import { verify } from 'crypto'; + /** @typedef {typeof import('../src/offer-up.contract.js').start} AssetContractFn */ @@ -48,8 +48,6 @@ const makeTestContext = async _t => { test.before(async t => (t.context = await makeTestContext(t))); -// IDEA: use test.serial and pass work products -// between tests using t.context. test('Install the contract', async t => { const { zoe, bundle } = t.context; @@ -64,7 +62,7 @@ test('Start the contract', async t => { const money = makeIssuerKit('PlayMoney'); const issuers = { Price: money.issuer }; - const terms = { tradePrice: AmountMath.make(money.brand, 5n) }; + const terms = { minBidPrice: AmountMath.make(money.brand, 5n) }; t.log('terms:', terms); /** @type {ERef>} */ @@ -83,14 +81,13 @@ test('Start the contract', async t => { * @param {Purse} purse * @param {string[]} choices */ -const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll'], bidValue = 25n * CENT, isMaxbid = false) => { +const alice = async (t, zoe, instance, purse, choices = ['map'], bidValue = 25n * CENT, isMaxbid = false) => { const publicFacet = E(zoe).getPublicFacet(instance); // @ts-expect-error Promise seems to work const terms = await E(zoe).getTerms(instance); - const { issuers, brands, tradePrice } = terms; - const bidPrice = AmountMath.make(tradePrice.brand, bidValue); - const zeroPrice = AmountMath.make(tradePrice.brand, 0n); - + const { brands, minBidPrice } = terms; + const bidPrice = AmountMath.make(minBidPrice.brand, bidValue); + const choiceBag = makeCopyBag(choices.map(name => [name, 1n])); const proposal = { give: { Price: bidPrice }, @@ -105,27 +102,12 @@ const alice = async (t, zoe, instance, purse, choices = ['map', 'scroll'], bidVa const seat = await E(zoe).offer(toTrade, proposal, { Price: pmt } ); return {seat, proposal, pmt}; - // const items = await E(seat).getPayout('Items') ; - // const actual = await E(issuers.Item).getAmountOf(items); - - // t.log('Alice payout brand', actual.brand); - // t.log('Alice payout value', actual.value); - // if (isMaxbid){ - // t.deepEqual(actual, proposal.want.Items); - // const pmtAmount = await E(purse).deposit(pmt); - // t.deepEqual(zeroPrice, pmtAmount); - // } else { - // t.notDeepEqual(actual, proposal.want.Items); - // const pmtAmount = await E(purse).deposit(pmt); - // t.notDeepEqual(zeroPrice, pmtAmount); - // } - }; const verifyBidSeat = async (t, seat, proposal, zoe, instance, purse, isMaxbid = false) => { const terms = await E(zoe).getTerms(instance); - const { issuers, brands, tradePrice } = terms; - const zeroPrice = AmountMath.make(tradePrice.brand, 0n); + const { issuers, minBidPrice } = terms; + const zeroPrice = AmountMath.make(minBidPrice.brand, 0n); if (isMaxbid){ @@ -143,51 +125,6 @@ const verifyBidSeat = async (t, seat, proposal, zoe, instance, purse, isMaxbid = }; -test('Alice trades: give some play money, want items', async t => { - const { zoe, bundle } = t.context; - - const money = makeIssuerKit('PlayMoney'); - const issuers = { Price: money.issuer }; - const terms = { tradePrice: AmountMath.make(money.brand, 5n) }; - - /** @type {ERef>} */ - const installation = E(zoe).install(bundle); - const { instance } = await E(zoe).startInstance(installation, issuers, terms); - t.log(instance); - t.is(typeof instance, 'object'); - - const alicePurse = money.issuer.makeEmptyPurse(); - const amountOfMoney = AmountMath.make(money.brand, 10n); - const moneyPayment = money.mint.mintPayment(amountOfMoney); - alicePurse.deposit(moneyPayment); - await alice(t, zoe, instance, alicePurse); -}); - -test('Trade in IST rather than play money', async t => { - /** - * Start the contract, providing it with - * the IST issuer. - * - * @param {{ zoe: ZoeService, bundle: {} }} powers - */ - const startContract = async ({ zoe, bundle }) => { - /** @type {ERef>} */ - const installation = E(zoe).install(bundle); - const feeIssuer = await E(zoe).getFeeIssuer(); - const feeBrand = await E(feeIssuer).getBrand(); - const tradePrice = AmountMath.make(feeBrand, 25n * CENT); - return E(zoe).startInstance( - installation, - { Price: feeIssuer }, - { tradePrice }, - ); - }; - - const { zoe, bundle, bundleCache, feeMintAccess } = t.context; - const { instance } = await startContract({ zoe, bundle }); - const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); - await alice(t, zoe, instance, await faucet(5n * UNIT6)); -}); test('use the code that will go on chain to start the contract', async t => { const noop = harden(() => {}); @@ -261,6 +198,8 @@ test('use the code that will go on chain to start the contract', async t => { const { feeMintAccess, bundleCache } = t.context; const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); await alice(t, zoe, instance, await faucet(5n * UNIT6)); + t.log(instance); + t.is(typeof instance, 'object'); }); @@ -271,11 +210,11 @@ test('bidSeats saved in Maps', async t => { const installation = E(zoe).install(bundle); const feeIssuer = await E(zoe).getFeeIssuer(); const feeBrand = await E(feeIssuer).getBrand(); - const tradePrice = AmountMath.make(feeBrand, 25n * CENT); + const minBidPrice = AmountMath.make(feeBrand, 25n * CENT); return E(zoe).startInstance( installation, { Price: feeIssuer }, - { tradePrice }, + { minBidPrice }, ); }; const { zoe, bundle, bundleCache, feeMintAccess } = t.context; @@ -291,3 +230,41 @@ test('bidSeats saved in Maps', async t => { await verifyBidSeat(t, seat2, proposal2, zoe, instance, purse2,false); await verifyBidSeat(t, seat3, proposal3, zoe, instance, purse3,false); }); + + + +test('bid on multiple Items', async t => { + + const startContract = async ({ zoe, bundle }) => { + /** @type {ERef>} */ + const installation = E(zoe).install(bundle); + const feeIssuer = await E(zoe).getFeeIssuer(); + const feeBrand = await E(feeIssuer).getBrand(); + const minBidPrice = AmountMath.make(feeBrand, 25n * CENT); + return E(zoe).startInstance( + installation, + { Price: feeIssuer }, + { minBidPrice }, + ); + }; + const { zoe, bundle, bundleCache, feeMintAccess } = t.context; + const { instance } = await startContract({ zoe, bundle }); + const { faucet } = makeStableFaucet({ bundleCache, feeMintAccess, zoe }); + const purse1 = await faucet(500n * UNIT6); + const purse2 = await faucet(500n * UNIT6); + const purse3 = await faucet(500n * UNIT6); + const {seat: seat1, proposal: proposal1} = await alice(t, zoe, instance, purse1 , ['map'], 6n* UNIT6); + const {seat: seat4, proposal: proposal4} = await alice(t, zoe, instance, purse1 , ['scroll'], 6n* UNIT6); + const {seat: seat5, proposal: proposal5} = await alice(t, zoe, instance, purse2 , ['scroll'], 9n* UNIT6); + const {seat: seat2, proposal:proposal2} = await alice(t, zoe, instance, purse2, ['map'], 7n* UNIT6, false); + const {seat: seat3, proposal:proposal3} = await alice(t, zoe, instance, purse3, ['map'], 5n* UNIT6); + const {seat: seat6, proposal: proposal6} = await alice(t, zoe, instance, purse3 , ['scroll'], 6n* UNIT6); + + await verifyBidSeat(t, seat1, proposal1, zoe, instance, purse1,false); + await verifyBidSeat(t, seat2, proposal2, zoe, instance, purse2,true); + await verifyBidSeat(t, seat3, proposal3, zoe, instance, purse3,false); + + await verifyBidSeat(t, seat4, proposal4, zoe, instance, purse1,false); + await verifyBidSeat(t, seat5, proposal5, zoe, instance, purse2,true); + await verifyBidSeat(t, seat6, proposal6, zoe, instance, purse3,false); +});