1import Tx from 'ethereumjs-tx';
2import * as azimuth from 'azimuth-js';
3import * as kg from 'urbit-key-generation/dist';
4import * as wg from './walletgen';
5import * as tank from './tank';
7import JSZip from 'jszip';
8import saveAs from 'file-saver';
10import { hexify } from './txn';
11import { deriveNetworkSeedFromUrbitWallet } from './keys';
12import { addHexPrefix } from './wallet';
13import { sendAndAwaitAll } from './txn';
14import { GAS_LIMITS } from './constants';
16// the initial network key revision is always 1
17const INITIAL_NETWORK_KEY_REVISION = 1;
19const TRANSACTION_STATES = {
21 label: 'Generating Transactions...',
25 label: 'Signing Transactions...',
29 label: 'Funding Transactions...',
33 label: 'Transferring Point...',
37 label: 'Cleaning Up...',
46export async function generateWallet(point) {
47 const ticket = await wg.makeTicket(point);
48 const wallet = await wg.generateWallet(point, ticket);
52//TODO should be moved to lib/walletgen
53export async function downloadWallet(paper) {
54 return new Promise((resolve, reject) => {
55 const zip = new JSZip();
56 //TODO the categories here aren't explained in bridge at all...
58 const bin0 = paper.filter(item => item.bin === '0');
59 const bin1 = paper.filter(item => item.bin === '1');
60 const bin2 = paper.filter(item => item.bin === '2');
61 const bin3 = paper.filter(item => item.bin === '3');
62 const bin4 = paper.filter(item => item.bin === '4');
64 const bin0Folder = zip.folder('0. Public');
65 const bin1Folder = zip.folder('1. Very High Friction Custody');
66 const bin2Folder = zip.folder('2. High Friction Custody');
67 const bin3Folder = zip.folder('3. Medium Friction Custody');
68 const bin4Folder = zip.folder('4. Low Friction Custody');
70 bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
71 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
72 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
73 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
74 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
76 zip.generateAsync({ type: 'blob' }).then(content => {
77 saveAs(content, 'urbit-wallet.zip');
83export async function reticketPointBetweenWallets({
92 const askForFunding = (address, minBalance, balance) =>
95 value: { address, minBalance, balance },
98 const gotFunding = () => onUpdate({ type: 'gotFunding' });
99 const progress = state => onUpdate({ type: 'progress', state });
102 // generate transactions
105 progress(TRANSACTION_STATES.GENERATING);
107 // transfer to invite wallet, so it can configure
108 //NOTE doing this in the invite wallet instead of the final wallet makes
109 // failure cases "solvable" by just telling the user to fully retry.
110 // in other words, the invite flow is effectively idempotent.
112 //TODO no harm done if we already owned it, but should still get a bool arg
113 // for skipping this, if it isn't too big a burden for callers
114 const transferTmpTx = azimuth.ecliptic.transferPoint(
120 transferTmpTx.gas = GAS_LIMITS.TRANSFER;
122 // configure networking public keys
123 const seed = await deriveNetworkSeedFromUrbitWallet(
125 INITIAL_NETWORK_KEY_REVISION
128 const networkSeed = seed.matchWith({
130 throw new Error('network seed not derived');
135 const networkKeys = kg.deriveNetworkKeys(networkSeed);
137 const keysTx = azimuth.ecliptic.configureKeys(
140 addHexPrefix(networkKeys.crypt.public),
141 addHexPrefix(networkKeys.auth.public),
145 keysTx.gas = GAS_LIMITS.CONFIGURE_KEYS;
147 // configure management proxy
149 const managementTx = azimuth.ecliptic.setManagementProxy(
152 toWallet.management.keys.address
154 managementTx.gas = GAS_LIMITS.SET_PROXY;
156 // set spawn & voting proxies situationally
158 let txs = [transferTmpTx, keysTx, managementTx];
160 azimuth.azimuth.getPointSize(point) !== azimuth.azimuth.PointSize.Planet
162 const spawnTx = azimuth.ecliptic.setSpawnProxy(
165 toWallet.spawn.keys.address
167 spawnTx.gas = GAS_LIMITS.SET_PROXY;
171 azimuth.azimuth.getPointSize(point) === azimuth.azimuth.PointSize.Galaxy
173 const votingTx = azimuth.ecliptic.setVotingProxy(
176 toWallet.voting.keys.address
178 votingTx.gas = GAS_LIMITS.SET_PROXY;
183 // transfer configured point to user's new wallet
185 const transferFinalTx = azimuth.ecliptic.transferPoint(
188 toWallet.ownership.keys.address,
191 transferFinalTx.gas = GAS_LIMITS.TRANSFER;
192 txs.push(transferFinalTx);
195 // finalizing & signing transactions
198 progress(TRANSACTION_STATES.SIGNING);
201 let inviteNonce = await web3.eth.getTransactionCount(fromWallet.address);
202 txs = txs.map(tx => {
203 tx.from = fromWallet.address;
204 tx.nonce = inviteNonce++;
205 tx.gasPrice = 20000000000; //NOTE we pay the premium for faster ux
206 totalCost = totalCost + tx.gasPrice * tx.gas;
210 let txPairs = txs.map(tx => {
211 let stx = new Tx(tx);
212 stx.sign(Buffer.from(fromWallet.privateKey, 'hex'));
214 raw: hexify(stx.serialize()),
220 // ensuring funding for transactions
223 progress(TRANSACTION_STATES.FUNDING);
225 const usedTank = await tank.ensureFundsFor(
230 txPairs.map(p => p.raw),
236 // sending and awaiting transactions
239 progress(TRANSACTION_STATES.TRANSFERRING);
241 await sendAndAwaitAll(web3, txPairs.map(p => p.signed), usedTank);
247 progress(TRANSACTION_STATES.CLEANING);
249 // if non-trivial eth left in invite wallet, transfer to new ownership
250 let balance = await web3.eth.getBalance(fromWallet.address);
251 const gasPrice = 20000000000;
252 const gasLimit = 21000;
253 const sendEthCost = gasPrice * gasLimit;
254 if (transferEth && balance > sendEthCost) {
255 const value = balance - sendEthCost;
257 to: toWallet.ownership.keys.address,
261 nonce: inviteNonce++,
263 let stx = new Tx(tx);
264 stx.sign(fromWallet.privateKey);
265 const rawTx = hexify(stx.serialize());
266 web3.eth.sendSignedTransaction(rawTx).catch(err => {
267 console.log('error sending value tx, who cares', err);
271 progress(TRANSACTION_STATES.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