1import { Just, Nothing } 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 { sendSignedTransaction } from './txn';
13import { BRIDGE_ERROR } from './error';
14import { attemptNetworkSeedDerivation } from './keys';
15import { addHexPrefix, WALLET_TYPES } from './wallet';
17const INVITE_STAGES = {
18 INVITE_LOGIN: 'invite login',
19 INVITE_WALLET: 'invite wallet',
20 INVITE_VERIFY: 'invite verify',
21 INVITE_TRANSACTIONS: 'invite transactions',
24const WALLET_STATES = {
25 UNLOCKING: 'Unlocking invite wallet',
26 GENERATING: 'Generating your wallet',
27 RENDERING: 'Creating paper collateral',
28 PAPER_READY: 'Download your wallet',
29 DOWNLOADED: 'Wallet downloaded',
30 TRANSACTIONS: 'Sending transactions',
33const TRANSACTION_STATES = {
35 label: 'Generating transactions',
39 label: 'Signing transactions',
43 label: 'Funding invite wallet',
47 label: 'Claiming invite',
51 label: 'Funding recipient wallet',
55 label: 'Configuring planet',
68async function generateWallet(point) {
69 const ticket = await wg.makeTicket(point);
70 const wallet = await wg.generateWallet(point, ticket);
74//TODO should be moved to lib/walletgen
75async function downloadWallet(paper) {
76 return new Promise((resolve, reject) => {
77 const zip = new JSZip();
78 //TODO the categories here aren't explained in bridge at all...
80 const bin0 = paper.filter(item => item.bin === '0');
81 const bin1 = paper.filter(item => item.bin === '1');
82 const bin2 = paper.filter(item => item.bin === '2');
83 const bin3 = paper.filter(item => item.bin === '3');
84 const bin4 = paper.filter(item => item.bin === '4');
86 const bin0Folder = zip.folder('0. Public');
87 const bin1Folder = zip.folder('1. Very High Friction Custody');
88 const bin2Folder = zip.folder('2. High Friction Custody');
89 const bin3Folder = zip.folder('3. Medium Friction Custody');
90 const bin4Folder = zip.folder('4. Low Friction Custody');
92 bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
93 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
94 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
95 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
96 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
98 zip.generateAsync({ type: 'blob' }).then(content => {
99 saveAs(content, 'urbit-wallet.zip');
105async function startTransactions(args) {
109 contracts: contractsM,
116 const askForFunding = (address, amount, current) => {
119 value: `Please make sure ${address} has at least ${amount} wei, we'll continue once that's true. Current balance: ${current}. Waiting`,
123 if (Nothing.hasInstance(web3M)) {
124 throw BRIDGE_ERROR.MISSING_WEB3;
126 const web3 = web3M.value;
128 if (Nothing.hasInstance(contractsM)) {
129 throw BRIDGE_ERROR.MISSING_CONTRACTS;
131 const contracts = contractsM.value;
133 if (Nothing.hasInstance(inviteWalletM)) {
134 throw BRIDGE_ERROR.MISSING_WALLET;
136 const inviteWallet = inviteWalletM.value;
138 if (Nothing.hasInstance(realWalletM)) {
139 throw BRIDGE_ERROR.MISSING_WALLET;
141 const realWallet = realWalletM.value;
143 // const realWallet = await urbitWalletFromTicket(args.realTicket, point);
145 if (Nothing.hasInstance(realPointM)) {
146 throw BRIDGE_ERROR.MISSING_POINT;
148 const point = realPointM.value;
150 const inviteAddress = inviteWallet.address;
151 console.log('working as', inviteAddress);
153 // transfer from invite wallet to new wallet
155 let transferTx = azimuth.ecliptic.transferPoint(
158 realWallet.ownership.keys.address
160 transferTx.gas = 500000; //TODO can maybe be lower?
162 // ping gas tank with txs if needed
164 let inviteNonce = await web3.eth.getTransactionCount(inviteAddress);
165 transferTx.nonce = inviteNonce++;
166 transferTx.gasPrice = 20000000000; //NOTE we pay the premium for faster ux
167 transferTx.from = inviteAddress;
169 //NOTE using web3.eth.accounts.signTransaction is broken (1.0.0-beta51)
170 let transferStx = new Tx(transferTx);
171 transferStx.sign(inviteWallet.privateKey);
172 let rawTransferStx = '0x' + transferStx.serialize().toString('hex');
174 const transferCost = transferTx.gas * transferTx.gasPrice;
177 value: TRANSACTION_STATES.FUNDING_INVITE,
179 let usedTank = await tank.ensureFundsFor(
190 value: TRANSACTION_STATES.CLAIMING,
194 await sendAndAwaitConfirm(web3, [Just(transferStx)], usedTank);
197 // we're gonna be operating as the new wallet from here on out, so change
198 // the relevant values
200 setUrbitWallet(Just(realWallet));
201 const newAddress = realWallet.ownership.keys.address;
203 // configure management proxy
205 let managementTx = azimuth.ecliptic.setManagementProxy(
208 realWallet.management.keys.address
210 managementTx.gas = 200000;
211 managementTx.nonce = 0;
213 // configure networking public keys
214 //TODO feels like more of this logic should live in a lib?
215 const seed = await attemptNetworkSeedDerivation(true, {
216 walletType: WALLET_TYPES.TICKET,
217 urbitWallet: Just(realWallet),
218 pointCursor: Just(point),
219 pointCache: { [point]: { keyRevisionNumber: 0 } },
222 const networkSeed = seed.matchWith({
224 throw new Error('network seed not derived');
229 const networkKeys = kg.deriveNetworkKeys(networkSeed);
231 let keysTx = azimuth.ecliptic.configureKeys(
234 addHexPrefix(networkKeys.crypt.public),
235 addHexPrefix(networkKeys.auth.public),
242 //TODO refactor this process into function
244 let txs = [managementTx, keysTx];
245 txs = txs.map(tx => {
246 tx.gasPrice = 20000000000;
247 tx.from = newAddress;
248 totalCost = totalCost + tx.gasPrice * tx.gas;
252 let txPairs = txs.map(tx => {
253 let stx = new Tx(tx);
254 stx.sign(Buffer.from(realWallet.ownership.keys.private, 'hex'));
256 raw: '0x' + stx.serialize().toString('hex'),
263 value: TRANSACTION_STATES.FUNDING_RECIPIENT,
265 usedTank = await tank.ensureFundsFor(
270 txPairs.map(p => p.raw),
276 value: TRANSACTION_STATES.CONFIGURING,
279 await sendAndAwaitConfirm(web3, txPairs.map(p => Just(p.signed)), usedTank);
283 value: TRANSACTION_STATES.CLEANING,
286 // if non-trivial eth left in invite wallet, transfer to new ownership
287 let balance = await web3.eth.getBalance(inviteAddress);
288 const gasPrice = 20000000000;
289 const gasLimit = 21000;
290 const sendEthCost = gasPrice * gasLimit;
291 if (balance > sendEthCost) {
292 const value = balance - sendEthCost;
293 console.log('sending', value);
299 nonce: inviteNonce++,
301 let stx = new Tx(tx);
302 stx.sign(inviteWallet.privateKey);
303 const rawTx = '0x' + stx.serialize().toString('hex');
304 web3.eth.sendSignedTransaction(rawTx).catch(err => {
305 console.log('error sending value tx, who cares', err);
307 console.log('sent old balance');
312 value: TRANSACTION_STATES.DONE,
315 // proceed without waiting for confirm
316 setUrbitWallet(Just(realWallet));
319async function sendAndAwaitConfirm(web3, signedTxs, usedTank) {
321 signedTxs.map(tx => {
322 return new Promise((resolve, reject) => {
323 sendSignedTransaction(web3, tx, usedTank, resolve);
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