@@ -12,6 +12,7 @@ import saveAs from 'file-saver';
1212import { sendSignedTransaction, hexify } from './txn';
1313import { attemptNetworkSeedDerivation } from './keys';
1414import { addHexPrefix, WALLET_TYPES } from './wallet';
15+import { GAS_LIMITS } from './constants';
1516
1617const INVITE_STAGES = {
1718 INVITE_LOGIN: 'invite login',
@@ -38,22 +39,14 @@ const TRANSACTION_STATES = {
3839 label: 'Signing Transactions...',
3940 progress: 0.15,
4041 },
41− FUNDING_INVITE: {
42− label: 'Funding Invite Wallet...',
42+ FUNDING: {
43+ label: 'Funding Transactions...',
4344 progress: 0.3,
4445 },
4546 CLAIMING: {
4647 label: 'Claiming Invite...',
4748 progress: 0.55,
4849 },
49− FUNDING_RECIPIENT: {
50− label: 'Funding Recipient Wallet...',
51− progress: 0.65,
52− },
53− CONFIGURING: {
54− label: 'Configuring Point...',
55− progress: 0.85,
56− },
5750 CLEANING: {
5851 label: 'Cleaning Up...',
5952 progress: 0.95,
@@ -108,6 +101,7 @@ async function claimPointFromInvite({
108101 web3,
109102 contracts,
110103 onUpdate,
104+ transferEth = false,
111105}) {
112106 const askForFunding = (address, minBalance, balance) =>
113107 onUpdate({
@@ -120,62 +114,29 @@ async function claimPointFromInvite({
120114
121115 const inviteAddress = inviteWallet.address;
122116
123− progress(TRANSACTION_STATES.GENERATING);
124−
125− // transfer from invite wallet to new wallet
126− const transferTx = azimuth.ecliptic.transferPoint(
127− contracts,
128− point,
129− wallet.ownership.keys.address
130− );
131− transferTx.gas = 500000; //TODO can maybe be lower?
132−
133− // ping gas tank with txs if needed
134− let inviteNonce = await web3.eth.getTransactionCount(inviteAddress);
135− transferTx.nonce = inviteNonce++;
136− transferTx.gasPrice = 20000000000; //NOTE we pay the premium for faster ux
137− transferTx.from = inviteAddress;
138−
139− progress(TRANSACTION_STATES.SIGNING);
140− // NOTE: using web3.eth.accounts.signTransaction is broken (1.0.0-beta51)
141− const transferStx = new Tx(transferTx);
142− transferStx.sign(inviteWallet.privateKey);
143− const rawTransferStx = hexify(transferStx.serialize());
144− const transferCost = transferTx.gas * transferTx.gasPrice;
145−
146− progress(TRANSACTION_STATES.FUNDING_INVITE);
147−
148− let usedTank = await tank.ensureFundsFor(
149− web3,
150− point,
151− inviteAddress,
152− transferCost,
153− [rawTransferStx],
154− askForFunding,
155− gotFunding
156− );
157−
158− progress(TRANSACTION_STATES.CLAIMING);
159−
160− // send transaction
161− await sendAndAwaitConfirm(web3, [Just(transferStx)], usedTank);
162−
163117 //
164− // we're gonna be operating as the new wallet from here on out, so change
165− // the relevant values
118+ // generate transactions
166119 //
167− const newAddress = wallet.ownership.keys.address;
168120
169− // configure management proxy
170− const managementTx = azimuth.ecliptic.setManagementProxy(
121+ progress(TRANSACTION_STATES.GENERATING);
122+
123+ // transfer to invite wallet, so it can configure
124+ //NOTE doing this in the invite wallet instead of the final wallet makes
125+ // failure cases "solvable" by just telling the user to fully retry.
126+ // in other words, the invite flow is effectively idempotent.
127+
128+ //TODO no harm done if we already owned it, but should still get a bool arg
129+ // for skipping this, if it isn't too big a burden for callers
130+ const transferTmpTx = azimuth.ecliptic.transferPoint(
171131 contracts,
172132 point,
173− wallet.management.keys.address
133+ inviteAddress,
134+ false
174135 );
175− managementTx.gas = 200000;
176− managementTx.nonce = 0;
136+ transferTmpTx.gas = GAS_LIMITS.TRANSFER;
177137
178138 // configure networking public keys
139+
179140 //TODO feels like more of this logic should live in a lib?
180141 const seed = await attemptNetworkSeedDerivation(true, {
181142 walletType: WALLET_TYPES.TICKET,
@@ -193,7 +154,7 @@ async function claimPointFromInvite({
193154
194155 const networkKeys = kg.deriveNetworkKeys(networkSeed);
195156
196− let keysTx = azimuth.ecliptic.configureKeys(
157+ const keysTx = azimuth.ecliptic.configureKeys(
197158 contracts,
198159 point,
199160 addHexPrefix(networkKeys.crypt.public),
@@ -201,43 +162,108 @@ async function claimPointFromInvite({
201162 1,
202163 false
203164 );
204− keysTx.gas = 150000;
205− keysTx.nonce = 1;
165+ keysTx.gas = GAS_LIMITS.CONFIGURE_KEYS;
166+
167+ // configure management proxy
168+
169+ const managementTx = azimuth.ecliptic.setManagementProxy(
170+ contracts,
171+ point,
172+ wallet.management.keys.address
173+ );
174+ managementTx.gas = GAS_LIMITS.SET_PROXY;
175+
176+ // set spawn & voting proxies situationally
177+
178+ let txs = [transferTmpTx, keysTx, managementTx];
179+ if (
180+ azimuth.azimuth.getPointSize(point) !== azimuth.azimuth.PointSize.Planet
181+ ) {
182+ const spawnTx = azimuth.ecliptic.setSpawnProxy(
183+ contracts,
184+ point,
185+ wallet.spawn.keys.address
186+ );
187+ spawnTx.gas = GAS_LIMITS.SET_PROXY;
188+ txs.push(spawnTx);
189+
190+ if (
191+ azimuth.azimuth.getPointSize(point) === azimuth.azimuth.PointSize.Galaxy
192+ ) {
193+ const votingTx = azimuth.ecliptic.setVotingProxy(
194+ contracts,
195+ point,
196+ wallet.voting.keys.address
197+ );
198+ votingTx.gas = GAS_LIMITS.SET_PROXY;
199+ txs.push(votingTx);
200+ }
201+ }
202+
203+ // transfer configured point to user's new wallet
204+
205+ const transferFinalTx = azimuth.ecliptic.transferPoint(
206+ contracts,
207+ point,
208+ wallet.ownership.keys.address,
209+ false
210+ );
211+ transferFinalTx.gas = GAS_LIMITS.TRANSFER;
212+ txs.push(transferFinalTx);
213+
214+ //
215+ // finalizing & signing transactions
216+ //
217+
218+ progress(TRANSACTION_STATES.SIGNING);
206219
207− //TODO refactor this process into function
208220 let totalCost = 0;
209− let txs = [managementTx, keysTx];
221+ let inviteNonce = await web3.eth.getTransactionCount(inviteAddress);
210222 txs = txs.map(tx => {
211− tx.gasPrice = 20000000000;
212− tx.from = newAddress;
223+ tx.from = inviteAddress;
224+ tx.nonce = inviteNonce++;
225+ tx.gasPrice = 20000000000; //NOTE we pay the premium for faster ux
213226 totalCost = totalCost + tx.gasPrice * tx.gas;
214227 return tx;
215228 });
216229
217230 let txPairs = txs.map(tx => {
218231 let stx = new Tx(tx);
219− stx.sign(Buffer.from(wallet.ownership.keys.private, 'hex'));
232+ stx.sign(Buffer.from(inviteWallet.privateKey, 'hex'));
220233 return {
221− raw: '0x' + stx.serialize().toString('hex'),
234+ raw: hexify(stx.serialize()),
222235 signed: stx,
223236 };
224237 });
225238
226− progress(TRANSACTION_STATES.FUNDING_RECIPIENT);
239+ //
240+ // ensuring funding for transactions
241+ //
242+
243+ progress(TRANSACTION_STATES.FUNDING);
227244
228− usedTank = await tank.ensureFundsFor(
245+ const usedTank = await tank.ensureFundsFor(
229246 web3,
230247 point,
231− newAddress,
248+ inviteAddress,
232249 totalCost,
233250 txPairs.map(p => p.raw),
234− askForFunding
251+ askForFunding,
252+ gotFunding
235253 );
236254
237− progress(TRANSACTION_STATES.CONFIGURING);
255+ //
256+ // sending and awaiting transactions
257+ //
258+
259+ progress(TRANSACTION_STATES.CLAIMING);
238260
239261 await sendAndAwaitConfirm(web3, txPairs.map(p => Just(p.signed)), usedTank);
240262
263+ //
264+ // move leftover eth
265+ //
266+
241267 progress(TRANSACTION_STATES.CLEANING);
242268
243269 // if non-trivial eth left in invite wallet, transfer to new ownership
@@ -245,11 +271,10 @@ async function claimPointFromInvite({
245271 const gasPrice = 20000000000;
246272 const gasLimit = 21000;
247273 const sendEthCost = gasPrice * gasLimit;
248− if (balance > sendEthCost) {
274+ if (transferEth && balance > sendEthCost) {
249275 const value = balance - sendEthCost;
250− console.log('sending', value);
251276 const tx = {
252− to: newAddress,
277+ to: wallet.ownership.keys.address,
253278 value: value,
254279 gasPrice: gasPrice,
255280 gas: gasLimit,
@@ -261,7 +286,6 @@ async function claimPointFromInvite({
261286 web3.eth.sendSignedTransaction(rawTx).catch(err => {
262287 console.log('error sending value tx, who cares', err);
263288 });
264− console.log('sent old balance');
265289 }
266290
267291 progress(TRANSACTION_STATES.DONE);