forked from urbit/azimuth-js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
delegatedSending.js
171 lines (151 loc) · 5.89 KB
/
delegatedSending.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* Delegated Sending
* @module delegatedSending
*/
const internal = require('./internal/delegatedSending');
const azimuth = require('./azimuth');
const ecliptic = require('./ecliptic');
/**
* Return the amount of invites left in the pool
* @param {Number} pool - Pool number
* @param {Number} prefix - Invites from this prefix
* @return {Promise<Number>} Number of invites remaining
*/
module.exports.pools = internal.pools
module.exports.invitesInPool = internal.pools
/**
* Return the points invited by point
* @param {Number} point - Point number
* @return {Promise<Array<Number>>} Points invited by point
*/
module.exports.getInvited = internal.getInvited
/**
* Return the point that point was invited by
* @param {Number} point - Point number
* @return {Promise<Number>} The inviter point (0 if not invited)
*/
module.exports.invitedBy = internal.invitedBy
/**
* Returns true if as can send point, false otherwise
* @param {Number} as - The inviter
* @param {Number} point - The point to send
* @return {Promise<Bool>} Whether as can send point
*/
module.exports.canSend = internal.canSend
/**
* Get the invite pool point belongs to
* @param {Number} point - Point number
* @return {Promise<Number>} Pool number
*/
module.exports.getPool = internal.getPool
module.exports.invitingFromPool = internal.getPool
/**
* Get the stars that have put invites into the pool
* @param {Number} pool - Pool number
* @return {Promise<Array<Number>>} Stars that touched the pool
*/
module.exports.getPoolStars = internal.getPoolStars
/**
* Returns true if receipients is eligible to receive a point, false otherwise
* @param {String} recipient - Ethereum address
* @return {Promise<Bool>} Whether recipient can receive a point
*/
module.exports.canReceive = internal.canReceive
/**
* Returns the total amount of usable invites available to point.
* Invites are usable if the star they're associated with has its spawn proxy
* set to the Delegated Sending contract, and is still under its spawn limit.
* @param {Number} point - The point whose invites to count
* @return {Promise<Number>} Total amount of invites
*/
module.exports.getTotalUsableInvites = async function(contracts, point) {
const pool = await internal.getPool(contracts, point);
const stars = await internal.getPoolStars(contracts, pool);
let counts = stars.map(async star => {
const capable = azimuth.isSpawnProxy(
contracts, star, contracts.delegatedSending.address
);
const supportive = azimuth.isLive(contracts, star);
if (!(await capable) || !(await supportive)) return 0;
const invites = await internal.pools(contracts, pool, star);
const spawnable = await ecliptic.getSpawnsRemaining(contracts, star);
return Math.min(invites, spawnable);
});
counts = await Promise.all(counts);
return counts.reduce((total, count) => (total + count), 0);
}
/**
* Generate a list of planets for as to send as invites
* NOTE that the returned list isn't guaranteed to contain exactly amount items,
* it may return fewer in cases where not enough invites are available,
* usable, or spawn limits are being hit
* @param {Number} as - point to send the planets with
* @param {Number} amount - amount of planets to generate
* @return {Promise<Array<Number>>} Pseudo-random list of planets that as can send
*/
module.exports.getPlanetsToSend = async function(contracts, as, amount) {
const sponsor = await azimuth.getSponsor(contracts, as);
const inviter = await internal.invitedBy(contracts, as);
const inviterSponsor = await azimuth.getSponsor(contracts, inviter);
const pool = await internal.getPool(contracts, as);
let stars = await internal.getPoolStars(contracts, pool);
// assign priorities so that we can order them:
// sponsor > inviter's sponsor > least spawned > most spawned
stars = stars.map(async star => {
const available = await internal.pools(contracts, pool, star);
const capable = azimuth.isSpawnProxy(
contracts, star, contracts.delegatedSending.address
);
const supportive = azimuth.isLive(contracts, star);
if (available === 0 || !(await capable) || !(await supportive))
return {star, available: 0};
const spawned = await azimuth.getSpawnCount(contracts, star);
let priority;
if (star === sponsor)
priority = -2;
else if (star === inviterSponsor)
priority = -1;
else if (priority === 0)
priority = spawned;
return {star: Number(star), available, priority};
});
stars = await Promise.all(stars);
stars = stars.filter(a => (a.available > 0));
stars = stars.sort((a, b) => (a.priority - b.priority));
let s = 0;
let planets = [];
while (amount > 0 && s < stars.length) {
let star = stars[s];
const spawnable = await ecliptic.getSpawnsRemaining(contracts, star.star);
const get = Math.min(star.available, spawnable, amount);
const unspawned = await azimuth.getUnspawnedChildren(contracts, star.star);
// make sure the first couple elements are randomized
for (let i = 0; i < get; i++) {
const j = Math.floor(Math.random() * unspawned.length);
[unspawned[i], unspawned[j]] = [unspawned[j], unspawned[i]];
}
// push number of planets to output list
for (let i = 0; i < get; i++) {
planets.push(unspawned[i]);
}
amount = amount - get;
s++;
}
return planets;
}
/**
* Give for (and their invite tree) access to size invites
* @param {Number} as - prefix to give invites as
* @param {Number} for - point to give invites to
* @param {Number} size - amount of invites to give
* @return {Object} An unsigned transaction object
*/
module.exports.setPoolSize = internal.setPoolSize
/**
* As as, send the point to to
* @param {Number} as - point to send the invite as
* @param {Number} point - the point to send as an invite
* @param {String} to - target Ethereum address
* @return {Object} An unsigned transaction object
*/
module.exports.sendPoint = internal.sendPoint