-
Notifications
You must be signed in to change notification settings - Fork 206
/
barterExchange.js
137 lines (120 loc) · 3.62 KB
/
barterExchange.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// @ts-check
import makeStore from '@agoric/store';
import '../../exported';
// Eventually will be importable from '@agoric/zoe-contract-support'
import { trade, satisfies } from '../contractSupport';
/**
* This Barter Exchange accepts offers to trade arbitrary goods for other
* things. It doesn't require registration of Issuers. If two offers satisfy
* each other, it exchanges the specified amounts in each side's want clause.
* https://agoric.com/documentation/zoe/guide/contracts/barter-exchange.html
*
* The Barter Exchange only accepts offers that look like
* { give: { In: amount }, want: { Out: amount} }
* The want amount will be matched, while the give amount is a maximum. Each
* successful trader gets their `want` and may trade with counter-parties who
* specify any amount up to their specified `give`.
*
* @type {ContractStartFn}
*/
const start = zcf => {
// bookOrders is a Map of Maps. The first key is the brand of the offer's
// GIVE, and the second key is the brand of its WANT. For each offer, we
// store its handle and the amounts for `give` and `want`.
const bookOrders = makeStore('bookOrders');
function lookupBookOrders(brandIn, brandOut) {
if (!bookOrders.has(brandIn)) {
bookOrders.init(brandIn, new Map());
}
const ordersMap = bookOrders.get(brandIn);
let ordersArray = ordersMap.get(brandOut);
if (!ordersArray) {
ordersArray = [];
ordersMap.set(brandOut, ordersArray);
}
return ordersArray;
}
function findMatchingTrade(newDetails, orders) {
return orders.find(order => {
return (
satisfies(zcf, newDetails.seat, { Out: order.amountIn }) &&
satisfies(zcf, order.seat, { Out: newDetails.amountIn })
);
});
}
function removeFromOrders(offerDetails) {
const orders = lookupBookOrders(
offerDetails.amountIn.brand,
offerDetails.amountOut.brand,
);
orders.splice(orders.indexOf(offerDetails), 1);
}
function tradeWithMatchingOffer(offerDetails) {
const orders = lookupBookOrders(
offerDetails.amountOut.brand,
offerDetails.amountIn.brand,
);
const matchingTrade = findMatchingTrade(offerDetails, orders);
if (matchingTrade) {
// reallocate by giving each side what it wants
trade(
zcf,
{
seat: matchingTrade.seat,
gains: {
Out: matchingTrade.amountOut,
},
losses: {
In: offerDetails.amountOut,
},
},
{
seat: offerDetails.seat,
gains: {
Out: offerDetails.amountOut,
},
losses: {
In: matchingTrade.amountOut,
},
},
);
removeFromOrders(matchingTrade);
offerDetails.seat.exit();
matchingTrade.seat.exit();
return true;
}
return false;
}
function addToBook(offerDetails) {
const orders = lookupBookOrders(
offerDetails.amountIn.brand,
offerDetails.amountOut.brand,
);
orders.push(offerDetails);
}
function extractOfferDetails(seat) {
const {
give: { In: amountIn },
want: { Out: amountOut },
} = seat.getProposal();
return {
seat,
amountIn,
amountOut,
};
}
/** @type {OfferHandler} */
const exchangeOfferHandler = seat => {
const offerDetails = extractOfferDetails(seat);
if (!tradeWithMatchingOffer(offerDetails)) {
addToBook(offerDetails);
}
return 'Trade completed.';
};
const publicFacet = harden({
makeInvitation: () => zcf.makeInvitation(exchangeOfferHandler, 'exchange'),
});
return { publicFacet };
};
harden(start);
export { start };