1import * as azimuth from 'azimuth-js';
2import * as tank from './tank';
4import { hexify } from './txn';
6 deriveNetworkSeedFromUrbitWallet,
10import { addHexPrefix } from './utils/address';
12 sendAndAwaitAllSerial,
14 sendSignedTransaction,
16import getSuggestedGasPrice from './getSuggestedGasPrice';
17import { GAS_LIMITS } from './constants';
18import { toBN } from 'web3-utils';
19import { safeToWei } from './lib';
20import { isPlanet } from './utils/point';
22// the initial network key revision is always 1
23const INITIAL_NETWORK_KEY_REVISION = 1;
25export const TRANSACTION_PROGRESS = {
34export async function reticketPointBetweenWallets({
45 nextRevision = INITIAL_NETWORK_KEY_REVISION,
49 const askForFunding = (address, minBalance, balance) =>
52 value: { address, minBalance, balance },
55 const gotFunding = () => onUpdate({ type: 'gotFunding' });
56 const progress = state => onUpdate({ type: 'progress', state });
59 // generate transactions
62 progress(TRANSACTION_PROGRESS.GENERATING);
64 // transfer to invite wallet, so it can configure
65 //NOTE doing this in the invite wallet instead of the final wallet makes
66 // failure cases "solvable" by just telling the user to fully retry.
67 // in other words, the invite flow is effectively idempotent.
69 //TODO no harm done if we already owned it, but should still get a bool arg
70 // for skipping this, if it isn't too big a burden for callers
71 const transferTmpTx = azimuth.ecliptic.transferPoint(
77 transferTmpTx.gas = GAS_LIMITS.TRANSFER;
79 // configure networking public keys
80 const seed = await deriveNetworkSeedFromUrbitWallet(toWallet, nextRevision);
82 const networkSeed = seed.matchWith({
84 throw new Error('network seed not derived');
89 const networkKeys = deriveNetworkKeys(networkSeed);
91 const keysTx = azimuth.ecliptic.configureKeys(
94 addHexPrefix(networkKeys.crypt.public),
95 addHexPrefix(networkKeys.auth.public),
97 /* discontinuous */ false
99 keysTx.gas = GAS_LIMITS.CONFIGURE_KEYS;
101 // configure management proxy
103 const managementTx = azimuth.ecliptic.setManagementProxy(
106 toWallet.management.keys.address
108 managementTx.gas = GAS_LIMITS.SET_PROXY;
110 // set spawn & voting proxies situationally
112 let txs = [transferTmpTx, keysTx, managementTx];
113 if (!isPlanet(point)) {
114 const spawnTx = azimuth.ecliptic.setSpawnProxy(
117 toWallet.spawn.keys.address
119 spawnTx.gas = GAS_LIMITS.SET_PROXY;
123 azimuth.azimuth.getPointSize(point) === azimuth.azimuth.PointSize.Galaxy
125 const votingTx = azimuth.ecliptic.setVotingProxy(
128 toWallet.voting.keys.address
130 votingTx.gas = GAS_LIMITS.SET_PROXY;
135 // transfer configured point to user's new wallet
137 const transferFinalTx = azimuth.ecliptic.transferPoint(
140 toWallet.ownership.keys.address,
143 transferFinalTx.gas = GAS_LIMITS.TRANSFER;
144 txs.push(transferFinalTx);
147 // finalizing & signing transactions
150 progress(TRANSACTION_PROGRESS.SIGNING);
152 const gasPriceGwei = (await getSuggestedGasPrice(networkType)).toFixed();
153 const chainId = await web3.eth.net.getId();
154 const gasPriceWeiBN = toBN(safeToWei(gasPriceGwei, 'gwei'));
155 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
156 const totalCost = txs.reduce(
157 (acc, tx) => acc.add(gasPriceWeiBN.mul(toBN(tx.gas))),
163 // Must be done in serial else hardware will give nonce errors
164 for (let i = 0; i < txs.length; i++) {
165 const stx = await signTransaction({
167 walletType: fromWalletType,
168 walletHdPath: fromWalletHdPath,
171 nonce: inviteNonce + i,
173 gasPrice: gasPriceGwei,
174 gasLimit: txs[i].gas,
179 raw: hexify(stx.serialize()),
184 inviteNonce = inviteNonce + txs.length;
187 // ensuring funding for transactions
190 progress(TRANSACTION_PROGRESS.FUNDING);
192 const usedTank = await tank.ensureFundsFor(
198 txPairs.map(p => p.raw),
204 // sending and awaiting transactions
207 progress(TRANSACTION_PROGRESS.TRANSFERRING);
209 const receipts = await sendAndAwaitAllSerial(
211 txPairs.map(p => p.signed),
215 const realCost = gasPriceWeiBN.mul(
216 receipts.reduce((sum, receipt) => {
217 return sum.add(toBN(receipt.gasUsed));
225 progress(TRANSACTION_PROGRESS.CLEANING);
227 let balance = toBN(await web3.eth.getBalance(fromWallet.address));
228 const gasLimit = GAS_LIMITS.SEND_ETH;
229 const sendEthCost = gasPriceWeiBN.mul(toBN(gasLimit));
231 // if we have funds left over from what the gas tank gave us, send those back
232 if (usedTank && realCost.add(sendEthCost).lt(totalCost)) {
234 const remainder = totalCost.sub(realCost).sub(sendEthCost);
235 console.log('returning to gas tank', remainder.toString());
236 const txn = { to: tank.TANK_ADDRESS, value: remainder };
237 const stx = await signTransaction({
239 walletType: fromWalletType,
240 walletHdPath: fromWalletHdPath,
244 gasPrice: gasPriceGwei,
248 sendSignedTransaction(web3, stx);
250 // only update these after the above succeeded
252 balance = balance.sub(remainder).sub(sendEthCost);
254 console.log('error returning gas, safely ignored:');
259 // if non-trivial eth left in invite wallet, transfer to new ownership
260 if (transferEth && balance.gt(sendEthCost)) {
262 const value = balance.sub(sendEthCost);
264 to: toWallet.ownership.keys.address,
267 const stx = await signTransaction({
269 walletType: fromWalletType,
270 walletHdPath: fromWalletHdPath,
274 gasPrice: gasPriceGwei,
278 sendSignedTransaction(web3, stx);
280 console.log('error sending value tx, safely ignored:');
285 progress(TRANSACTION_PROGRESS.DONE);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list