1import { Just } from 'folktale/maybe';
2import Tx from 'ethereumjs-tx';
4import * as azimuth from 'azimuth-js';
5import * as kg from 'urbit-key-generation/dist';
6import * as wg from './walletgen';
7import * as tank from './tank';
9import JSZip from 'jszip';
10import saveAs from 'file-saver';
12import { hexify, sendAndAwaitConfirm } from './txn';
13import { deriveNetworkSeedFromUrbitWallet } from './keys';
14import { addHexPrefix } from './wallet';
15import { GAS_LIMITS } from './constants';
17// the initial network key revision is always 1
18const INITIAL_NETWORK_KEY_REVISION = 1;
20const TRANSACTION_STATES = {
22 label: 'Generating Transactions...',
26 label: 'Signing Transactions...',
30 label: 'Funding Transactions...',
34 label: 'Claiming Invite...',
38 label: 'Cleaning Up...',
47export async function generateWallet(point) {
48 const ticket = await wg.makeTicket(point);
49 const wallet = await wg.generateWallet(point, ticket);
53//TODO should be moved to lib/walletgen
54export async function downloadWallet(paper) {
55 return new Promise((resolve, reject) => {
56 const zip = new JSZip();
57 //TODO the categories here aren't explained in bridge at all...
59 const bin0 = paper.filter(item => item.bin === '0');
60 const bin1 = paper.filter(item => item.bin === '1');
61 const bin2 = paper.filter(item => item.bin === '2');
62 const bin3 = paper.filter(item => item.bin === '3');
63 const bin4 = paper.filter(item => item.bin === '4');
65 const bin0Folder = zip.folder('0. Public');
66 const bin1Folder = zip.folder('1. Very High Friction Custody');
67 const bin2Folder = zip.folder('2. High Friction Custody');
68 const bin3Folder = zip.folder('3. Medium Friction Custody');
69 const bin4Folder = zip.folder('4. Low Friction Custody');
71 bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
72 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
73 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
74 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
75 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
77 zip.generateAsync({ type: 'blob' }).then(content => {
78 saveAs(content, 'urbit-wallet.zip');
84export async function claimPointFromInvite({
93 const askForFunding = (address, minBalance, balance) =>
96 value: { address, minBalance, balance },
99 const gotFunding = () => onUpdate({ type: 'gotFunding' });
100 const progress = state => onUpdate({ type: 'progress', state });
102 const inviteAddress = inviteWallet.address;
105 // generate transactions
108 progress(TRANSACTION_STATES.GENERATING);
110 // transfer to invite wallet, so it can configure
111 //NOTE doing this in the invite wallet instead of the final wallet makes
112 // failure cases "solvable" by just telling the user to fully retry.
113 // in other words, the invite flow is effectively idempotent.
115 //TODO no harm done if we already owned it, but should still get a bool arg
116 // for skipping this, if it isn't too big a burden for callers
117 const transferTmpTx = azimuth.ecliptic.transferPoint(
123 transferTmpTx.gas = GAS_LIMITS.TRANSFER;
125 // configure networking public keys
126 const seed = await deriveNetworkSeedFromUrbitWallet(
128 INITIAL_NETWORK_KEY_REVISION
131 const networkSeed = seed.matchWith({
133 throw new Error('network seed not derived');
138 const networkKeys = kg.deriveNetworkKeys(networkSeed);
140 const keysTx = azimuth.ecliptic.configureKeys(
143 addHexPrefix(networkKeys.crypt.public),
144 addHexPrefix(networkKeys.auth.public),
148 keysTx.gas = GAS_LIMITS.CONFIGURE_KEYS;
150 // configure management proxy
152 const managementTx = azimuth.ecliptic.setManagementProxy(
155 wallet.management.keys.address
157 managementTx.gas = GAS_LIMITS.SET_PROXY;
159 // set spawn & voting proxies situationally
161 let txs = [transferTmpTx, keysTx, managementTx];
163 azimuth.azimuth.getPointSize(point) !== azimuth.azimuth.PointSize.Planet
165 const spawnTx = azimuth.ecliptic.setSpawnProxy(
168 wallet.spawn.keys.address
170 spawnTx.gas = GAS_LIMITS.SET_PROXY;
174 azimuth.azimuth.getPointSize(point) === azimuth.azimuth.PointSize.Galaxy
176 const votingTx = azimuth.ecliptic.setVotingProxy(
179 wallet.voting.keys.address
181 votingTx.gas = GAS_LIMITS.SET_PROXY;
186 // transfer configured point to user's new wallet
188 const transferFinalTx = azimuth.ecliptic.transferPoint(
191 wallet.ownership.keys.address,
194 transferFinalTx.gas = GAS_LIMITS.TRANSFER;
195 txs.push(transferFinalTx);
198 // finalizing & signing transactions
201 progress(TRANSACTION_STATES.SIGNING);
204 let inviteNonce = await web3.eth.getTransactionCount(inviteAddress);
205 txs = txs.map(tx => {
206 tx.from = inviteAddress;
207 tx.nonce = inviteNonce++;
208 tx.gasPrice = 20000000000; //NOTE we pay the premium for faster ux
209 totalCost = totalCost + tx.gasPrice * tx.gas;
213 let txPairs = txs.map(tx => {
214 let stx = new Tx(tx);
215 stx.sign(Buffer.from(inviteWallet.privateKey, 'hex'));
217 raw: hexify(stx.serialize()),
223 // ensuring funding for transactions
226 progress(TRANSACTION_STATES.FUNDING);
228 const usedTank = await tank.ensureFundsFor(
233 txPairs.map(p => p.raw),
239 // sending and awaiting transactions
242 progress(TRANSACTION_STATES.CLAIMING);
244 await sendAndAwaitConfirm(web3, txPairs.map(p => Just(p.signed)), usedTank);
250 progress(TRANSACTION_STATES.CLEANING);
252 // if non-trivial eth left in invite wallet, transfer to new ownership
253 let balance = await web3.eth.getBalance(inviteAddress);
254 const gasPrice = 20000000000;
255 const gasLimit = 21000;
256 const sendEthCost = gasPrice * gasLimit;
257 if (transferEth && balance > sendEthCost) {
258 const value = balance - sendEthCost;
260 to: wallet.ownership.keys.address,
264 nonce: inviteNonce++,
266 let stx = new Tx(tx);
267 stx.sign(inviteWallet.privateKey);
268 const rawTx = hexify(stx.serialize());
269 web3.eth.sendSignedTransaction(rawTx).catch(err => {
270 console.log('error sending value tx, who cares', err);
274 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