diff --git a/package-lock.json b/package-lock.json index a26dbd22b..b1328f5b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4823,9 +4823,9 @@ } }, "@open-rpc/client-js": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.7.0.tgz", - "integrity": "sha512-cRGJbXTgdhJNU49vWzJIATRmKBLP2x6tuHJzX9Jg3N8f1VEkge0riUEek2LFIrZiM4TdUp8XV4Ns1W0SZzdfSw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.7.1.tgz", + "integrity": "sha512-DycSYZUGSUwFl+k9T8wLBSGA8f2hYkvS5A9AB94tBOuU8QlP468NS5ZtAxy72dF4g2WW0genwNJdfeFnHnaxXQ==", "requires": { "isomorphic-fetch": "^3.0.0", "isomorphic-ws": "^4.0.1", @@ -7764,11 +7764,11 @@ } }, "@urbit/roller-api": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@urbit/roller-api/-/roller-api-0.1.8.tgz", - "integrity": "sha512-4Wapq9RyQc/oktahTAj4OjS9ry7fXiF1AtZ0mnSDcbIanrJ2+x5bYgJTEJ2ymIM9AT1mEhG0PT/Sz31WkieGOA==", + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@urbit/roller-api/-/roller-api-0.1.9.tgz", + "integrity": "sha512-pl33XA0eLumWC/JauATsSnz29auUP3Thwx3n2KTRd112/lXsWc4Yla6ib8lYDS1cNZ0/r9ArO2pEA/M6GAryTQ==", "requires": { - "@open-rpc/client-js": "1.7.0", + "@open-rpc/client-js": "1.7.1", "@open-rpc/meta-schema": "1.14.2", "@open-rpc/schema-utils-js": "1.15.0", "@types/json-schema": "7.0.3", diff --git a/package.json b/package.json index 09a031c95..bb3c121f1 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@types/styled-components": "^5.1.10", "@types/styled-system": "^5.1.11", "@types/styled-system__css": "^5.0.15", - "@urbit/roller-api": "^0.1.8", + "@urbit/roller-api": "^0.1.9", "@walletconnect/client": "^1.6.2", "@walletconnect/qrcode-modal": "^1.6.2", "@welldone-software/why-did-you-render": "^3.2.3", diff --git a/src/lib/useRoller.ts b/src/lib/useRoller.ts index 05ee6076f..1f1043d91 100644 --- a/src/lib/useRoller.ts +++ b/src/lib/useRoller.ts @@ -7,11 +7,9 @@ import { Just } from 'folktale/maybe'; import { Invite } from 'lib/types/Invite'; import { convertToInt } from './convertToInt'; -import { ensureHexPrefix } from 'form/formatters'; import { isDevelopment, isRopsten } from './flags'; import { hasPoint, generateInviteWallet } from './utils/roller'; import { ROLLER_HOSTS } from './constants'; -import { signTransactionHash } from './authToken'; import { useNetwork } from 'store/network'; import { usePointCursor } from 'store/pointCursor'; @@ -25,16 +23,16 @@ import { import { usePointCache } from 'store/pointCache'; import { getOutgoingPoints } from 'views/Points'; import { - getSpawnNonce, - getOwnerNonce, - getManagementNonce, -} from './utils/nonce'; + getSpawnProxy, + getManagerProxy, + getAddressProxy, + getTransferProxy, +} from './utils/proxy'; import { isPlanet } from './utils/point'; import { - deriveNetworkKeys, - CRYPTO_SUITE_VERSION, deriveNetworkSeedFromUrbitWallet, + attemptNetworkSeedDerivation, } from 'lib/keys'; import { @@ -45,8 +43,6 @@ import { Options, EthAddress, UnspawnedPoints, - From, - Hash, SpawnParams, L2Data, } from '@urbit/roller-api'; @@ -55,7 +51,7 @@ import { configureKeys, getTimeToNextBatch, spawn, - transferPoint, + transferPointRequest, registerProxyAddress, } from './utils/roller'; @@ -68,7 +64,7 @@ function isShipNumber(ship: number | string | undefined): ship is number { } export default function useRoller() { - const { wallet, authToken, authMnemonic }: any = useWallet(); + const { wallet, authToken, authMnemonic, urbitWallet }: any = useWallet(); const { pointCursor }: any = usePointCursor(); const { web3, contracts }: any = useNetwork(); const allPoints: any = usePointCache(); @@ -82,8 +78,6 @@ export default function useRoller() { setPendingTransactions, setInvites, currentL2, - nonces, - increaseNonce, } = useRollerStore(); const [config, setConfig] = useState(null); @@ -161,12 +155,9 @@ export default function useRoller() { new Set() ); - console.log(pendingTxs, pendingSpawns); - planets = planets.filter((point: number) => !pendingSpawns.has(point)); - const starInfo = nonces[_point]; - console.log(starInfo); + const starInfo = await api.getPoint(_point); const tickets: { ticket: string; planet: number; @@ -174,56 +165,62 @@ export default function useRoller() { }[] = []; const requests: Promise[] = []; - const operator = - getSpawnNonce(starInfo, _wallet.address) || - getOwnerNonce(starInfo, _wallet.address); + const proxy = getSpawnProxy(starInfo.ownership!, _wallet.address); - if (operator === undefined) + if (proxy === undefined) throw new Error("Error: Address doesn't match proxy"); - console.log('operator', operator); + + const nonce = await api.getNonce({ + ship: _point, + proxy, + }); + for (let i = 0; i < numInvites && planets[i]; i++) { const planet = planets[i]; - const nonceInc = i + operator.nonce!; + const nonceInc = i + nonce; const { ticket, inviteWallet } = await generateInviteWallet( planet, _authToken ); - const spawnRequest = await spawn( + const spawnRequest = spawn( api, _wallet, _point, - operator.proxy!, + proxy, nonceInc, planet ); - increaseNonce(_point, operator.proxy!); - const configureKeysRequest = await configureKeys( + const networkSeed = await deriveNetworkSeedFromUrbitWallet( + inviteWallet, + 1 + ); + const configureKeysRequest = configureKeys( api, _wallet, planet, - operator.proxy!, + 'own', 0, - inviteWallet + networkSeed ); - const setManagementProxyRequest = await registerProxyAddress( + const setManagementProxyRequest = registerProxyAddress( api, _wallet, planet, - operator.proxy!, + 'own', 'manage', 1, inviteWallet.management.keys.address ); - const transferPointRequest = await transferPoint( + const transferRequest = transferPointRequest( api, _wallet, planet, - operator.proxy!, + 'own', 2, inviteWallet.ownership.keys.address ); @@ -232,7 +229,7 @@ export default function useRoller() { spawnRequest, configureKeysRequest, setManagementProxyRequest, - transferPointRequest + transferRequest ); tickets.push({ ticket, @@ -249,7 +246,6 @@ export default function useRoller() { status: 'pending', })); - console.log('latest update', nonces[_point]); setPendingInvites(_point, pendingInvites); }, [ @@ -261,8 +257,6 @@ export default function useRoller() { web3, getDetails, authMnemonic, - nonces, - increaseNonce, ] ); @@ -412,16 +406,63 @@ export default function useRoller() { ] ); - // TODO: extract from #transferPoint - // interface ConfigureKeysParams { + interface ConfigureKeysParams { + breach: boolean; + customNetworkSeed?: string; + } + + const configureNetworkingKeys = async ({ + breach, + customNetworkSeed, + }: ConfigureKeysParams) => { + const _point = need.point(pointCursor); + const azimuthPoint = await api.getPoint(_point); + const _wallet = wallet.getOrElse(null); + const _details = getDetails(_point); + if (!_wallet || !_details) { + // not using need because we want a custom error + throw new Error('Internal Error: Missing Wallet/Details'); + } - // } + const pointDetails = await api.getPoint(_point); + const proxy = getManagerProxy(pointDetails.ownership!, _wallet.address); - // const configureKeys = async () => { + if (proxy === undefined) + throw new Error("Error: Address doesn't match proxy"); - // } + const nonce = await api.getNonce({ + ship: _point, + proxy, + }); - interface AcceptInviteParams { + const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10); + const nextRevision = networkRevision + 1; + const networkSeed = customNetworkSeed + ? customNetworkSeed + : await attemptNetworkSeedDerivation({ + urbitWallet, + wallet, + authMnemonic, + details: _details, + point: _point, + authToken, + revision: nextRevision, + }); + const txHash = await configureKeys( + api, + _wallet, + _point, + proxy!, + nonce, + networkSeed, + breach + ); + const pendingTx = await api.getPendingTx(txHash); + + return pendingTx; + }; + + interface ReticketParams { point: Ship; to: EthAddress; manager: EthAddress; @@ -429,95 +470,81 @@ export default function useRoller() { toWallet: any; // TODO: wallet type } - const acceptInvite = async ({ + const performL2Reticket = async ({ point, to, manager, fromWallet, toWallet, - }: AcceptInviteParams) => { + }: ReticketParams) => { const azimuthPoint = await api.getPoint(point); - console.log('azimuthPoint', azimuthPoint); - const ownerAddress = azimuthPoint?.ownership?.owner?.address!; - - const operator = - azimuthPoint.ownership && - getOwnerNonce(azimuthPoint.ownership, ownerAddress); - - if (operator === undefined) - throw new Error("Error: Address doesn't match proxy"); + const proxy = 'own'; + let nonce = await api.getNonce({ ship: point, proxy }); - const nonce = operator.nonce!; + let requests = []; // 1. Update networking keys - // TODO: extract this to a useRoller#configureKeys function? const networkRevision = convertToInt(azimuthPoint.network.keys.life, 10); const nextRevision = networkRevision + 1; - const fromOwnerProxy: From = { - ship: point, - proxy: 'own', - }; - - const seed = await deriveNetworkSeedFromUrbitWallet(toWallet, nextRevision); - const keys = deriveNetworkKeys(seed.value); - const keysData = { - auth: ensureHexPrefix(keys.auth.public), - breach: false, - cryptoSuite: CRYPTO_SUITE_VERSION.toString(), - encrypt: ensureHexPrefix(keys.crypt.public), - }; - - const keysHash: Hash = await api.hashTransaction( - nonce, - fromOwnerProxy, - 'configureKeys', - keysData + const networkSeed = await deriveNetworkSeedFromUrbitWallet( + toWallet, + nextRevision ); - - const keysSigningKey = fromWallet.privateKey; - await api.configureKeys( - signTransactionHash(keysHash, Buffer.from(keysSigningKey, 'hex')), - fromOwnerProxy, - ownerAddress, - keysData + const configureKeysRequest = await configureKeys( + api, + fromWallet, + point, + proxy, + nonce, + networkSeed ); - // TODO: not needed? next time we operate with this point we will login, and get - // the latest nonce (updated with any nonce increase from possible pending txs) - // increaseNonce(point, operator.proxy!); + nonce = nonce + 1; + requests.push(configureKeysRequest); // 2. Set Management Proxy - await registerProxyAddress( + const registerMgmtRequest = await registerProxyAddress( api, fromWallet, point, - operator.proxy!, + proxy, 'manage', - nonce + 1, + nonce, manager ); + nonce = nonce + 1; + requests.push(registerMgmtRequest); - // 3. Transfer point - const transferData = { address: to, reset: false }; - const transferHash: Hash = await api.hashTransaction( - nonce + 2, - fromOwnerProxy, - 'transferPoint', - transferData - ); - - const transferTxHash = await api.transferPoint( - signTransactionHash(transferHash, Buffer.from(keysSigningKey, 'hex')), - fromOwnerProxy, - ownerAddress, - transferData + // 3. Set Spawn Proxy + if (!isPlanet(Number(point))) { + const registerSpawnRequest = await registerProxyAddress( + api, + fromWallet, + point, + proxy, + 'spawn', + nonce, + toWallet.spawn.keys.address + ); + nonce = nonce + 1; + requests.push(registerSpawnRequest); + } + // 4. Transfer point + const transferTxRequest = await transferPointRequest( + api, + fromWallet, + point, + proxy, + nonce, + to ); + requests.push(transferTxRequest); - return transferTxHash; + const hashes = await Promise.all(requests); + return hashes; }; const setProxyAddress = useCallback( async (proxyType: Proxy, address: EthAddress) => { - console.log(proxyType, address); const _point = need.point(pointCursor); const _wallet = wallet.getOrElse(null); @@ -526,21 +553,22 @@ export default function useRoller() { throw new Error('Internal Error: Missing Wallet'); } - const pointDetails = nonces[_point]; - const operator = - getManagementNonce(pointDetails, _wallet.address) || - getOwnerNonce(pointDetails, _wallet.address); + const pointDetails = await api.getPoint(_point); + const ownership = pointDetails.ownership!; + const proxy = getAddressProxy(ownership, _wallet.address, proxyType); - if (operator === undefined) - throw new Error("Error: Address doesn't match proxy"); + if (proxy === undefined) + throw new Error("Error: Address doesn't match expected proxy"); + + const nonce = await api.getNonce({ ship: _point, proxy }); const txHash = await registerProxyAddress( api, _wallet, _point, - operator.proxy!, + proxy, proxyType, - operator.nonce!, + nonce, address ); @@ -548,7 +576,41 @@ export default function useRoller() { return pendingTx; }, - [api, pointCursor, wallet, nonces] + [api, pointCursor, wallet] + ); + + const transferPoint = useCallback( + async (address: EthAddress, reset?: boolean) => { + const _point = need.point(pointCursor); + const _wallet = wallet.getOrElse(null); + + if (!_wallet) { + // not using need because we want a custom error + throw new Error('Internal Error: Missing Wallet'); + } + + const pointDetails = await api.getPoint(_point); + const proxy = getTransferProxy(pointDetails.ownership!, _wallet.address); + + if (proxy === undefined) + throw new Error("Error: Address doesn't match proxy"); + + const nonce = await api.getNonce({ ship: _point, proxy }); + + const txHash = await transferPointRequest( + api, + _wallet, + _point, + proxy, + nonce, + address, + reset || false + ); + const pendingTx = await api.getPendingTx(txHash); + + return pendingTx; + }, + [api, pointCursor, wallet] ); // On load, get initial config @@ -579,7 +641,7 @@ export default function useRoller() { return { api, - acceptInvite, + performL2Reticket, config, getPoints, getInvites, @@ -587,5 +649,6 @@ export default function useRoller() { generateInviteCodes, transferPoint, setProxyAddress, + configureNetworkingKeys, }; } diff --git a/src/lib/utils/nonce.ts b/src/lib/utils/nonce.ts deleted file mode 100644 index e8fd436ec..000000000 --- a/src/lib/utils/nonce.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { Ownership } from '@urbit/roller-api'; - -export const getProxyAndNonce = ( - owner: Ownership, - address: string -): { proxy?: string; nonce?: number } => - owner.managementProxy?.address === address - ? { proxy: 'manage', nonce: owner.managementProxy.nonce } - : owner.owner?.address === address - ? { proxy: 'own', nonce: owner.owner.nonce } - : owner.spawnProxy?.address === address - ? { proxy: 'spawn', nonce: owner.spawn?.owner.nonce } - : owner.votingProxy?.address === address - ? { proxy: 'vote', nonce: owner.votingProxy?.owner.nonce } - : owner.transferProxy?.address === address - ? { - proxy: 'transfer', - nonce: owner.votingProxy?.nonce, - } - : { proxy: undefined, nonce: undefined }; - -export const getManagementNonce = ( - owner: Ownership, - address: string -): { nonce?: number; proxy?: string } | undefined => - owner.managementProxy?.address === address - ? { nonce: owner.managementProxy.nonce, proxy: 'manage' } - : undefined; - -export const getTransferNonce = ( - owner: Ownership, - address: string -): { nonce?: number; proxy?: string } | undefined => - owner.transferProxy?.address === address - ? { nonce: owner.transferProxy.nonce, proxy: 'transfer' } - : undefined; - -export const getOwnerNonce = ( - owner: Ownership, - address: string -): { nonce?: number; proxy?: string } | undefined => - owner.owner?.address === address - ? { nonce: owner.owner.nonce, proxy: 'own' } - : undefined; - -export const getSpawnNonce = ( - owner: Ownership, - address: string -): { nonce?: number; proxy?: string } | undefined => - owner.spawnProxy?.address === address - ? { nonce: owner.spawnProxy.nonce, proxy: 'spawn' } - : undefined; - -export const getVotingNonce = ( - owner: Ownership, - address: string -): { nonce?: number; proxy?: string } | undefined => - owner.votingProxy?.address === address - ? { nonce: owner.votingProxy.nonce, proxy: 'vote' } - : undefined; - -export const increaseProxyNonce = ( - owner: Ownership, - proxy: string -): Ownership | undefined => - 'manage' === proxy - ? { - ...owner, - managementProxy: { - ...owner.managementProxy, - nonce: owner.managementProxy?.nonce - ? owner.managementProxy?.nonce + 1 - : 1, - }, - } - : 'own' === proxy - ? { - ...owner, - owner: { - ...owner.owner, - nonce: owner.owner?.nonce ? owner.owner?.nonce + 1 : 1, - }, - } - : 'spawn' === proxy - ? { - ...owner, - spawnProxy: { - ...owner.spawnProxy, - nonce: owner.spawnProxy?.nonce ? owner.spawnProxy?.nonce + 1 : 1, - }, - } - : 'vote' === proxy - ? { - ...owner, - votingProxy: { - ...owner.votingProxy, - nonce: owner.votingProxy?.nonce ? owner.votingProxy?.nonce + 1 : 1, - }, - } - : 'transfer' === proxy - ? { - ...owner, - transferProxy: { - ...owner.transferProxy, - nonce: owner.transferProxy?.nonce - ? owner.transferProxy?.nonce + 1 - : 1, - }, - } - : undefined; - -export const setProxyNonce = ( - owner: Ownership, - proxy: string, - nonce: number -): Ownership | undefined => - 'manage' === proxy - ? { - ...owner, - managementProxy: { - ...owner.managementProxy, - nonce, - }, - } - : 'own' === proxy - ? { - ...owner, - owner: { - ...owner.owner, - nonce, - }, - } - : 'spawn' === proxy - ? { - ...owner, - spawnProxy: { - ...owner.spawnProxy, - nonce, - }, - } - : 'vote' === proxy - ? { - ...owner, - votingProxy: { - ...owner.voteProxy, - nonce, - }, - } - : 'transfer' === proxy - ? { - ...owner, - transferProxy: { - ...owner.transferProxy, - nonce, - }, - } - : undefined; diff --git a/src/lib/utils/proxy.ts b/src/lib/utils/proxy.ts new file mode 100644 index 000000000..d2c3879fd --- /dev/null +++ b/src/lib/utils/proxy.ts @@ -0,0 +1,49 @@ +import { Ownership, Proxy } from '@urbit/roller-api'; + +const isManagementProxy = (owner: Ownership, address: string) => + owner.managementProxy?.address === address; + +const isTransferProxy = (owner: Ownership, address: string) => + owner.transferProxy?.address === address; + +const isSpawnProxy = (owner: Ownership, address: string) => + owner.spawnProxy?.address === address; + +const isOwnerProxy = (owner: Ownership, address: string) => + owner.owner?.address === address; + +export const getSpawnProxy = (owner: Ownership, address: string) => + isOwnerProxy(owner, address) + ? 'own' + : isSpawnProxy(owner, address) + ? 'spawn' + : undefined; + +export const getManagerProxy = (owner: Ownership, address: string) => + isOwnerProxy(owner, address) + ? 'own' + : isManagementProxy(owner, address) + ? 'manage' + : undefined; + +export const getAddressProxy = ( + owner: Ownership, + address: string, + proxyType: Proxy +) => + isOwnerProxy(owner, address) + ? 'own' + : proxyType === 'manage' && isManagementProxy(owner, address) + ? 'manage' + : proxyType === 'spawn' && isSpawnProxy(owner, address) + ? 'spawn' + : proxyType === 'transfer' && isTransferProxy(owner, address) + ? 'transfer' + : undefined; + +export const getTransferProxy = (owner: Ownership, address: string) => + isOwnerProxy(owner, address) + ? 'own' + : isTransferProxy(owner, address) + ? 'transfer' + : undefined; diff --git a/src/lib/utils/roller.ts b/src/lib/utils/roller.ts index 99d922901..14f26d57c 100644 --- a/src/lib/utils/roller.ts +++ b/src/lib/utils/roller.ts @@ -3,7 +3,7 @@ import { Invite } from 'lib/types/Invite'; import { Just, Nothing } from 'folktale/maybe'; import { randomHex } from 'web3-utils'; -import { +import RollerRPCAPI, { Proxy, Signature, From, @@ -12,11 +12,7 @@ import { Ship, } from '@urbit/roller-api'; -import { - deriveNetworkSeedFromUrbitWallet, - deriveNetworkKeys, - CRYPTO_SUITE_VERSION, -} from 'lib/keys'; +import { deriveNetworkKeys, CRYPTO_SUITE_VERSION } from 'lib/keys'; import { addHexPrefix } from 'lib/utils/address'; import { makeDeterministicTicket, generateWallet } from 'lib/walletgen'; @@ -27,6 +23,9 @@ export const HOUR = MINUTE * 60; export const padZero = (amount: number) => `${amount < 10 && amount > 0 ? '0' : ''}${amount}`; +export const hasPoint = (point: number) => (invite: Invite) => + invite.planet === point; + export const getTimeToNextBatch = (nextBatch: number, now: number) => { const toNext = nextBatch - now; const hours = Math.floor(toNext / HOUR); @@ -50,7 +49,7 @@ export const generateInviteWallet = async (point: number, seed: string) => { }; export const spawn = async ( - api: any, + api: RollerRPCAPI, _wallet: any, _point: number, proxy: string, @@ -73,19 +72,19 @@ export const spawn = async ( }; export const configureKeys = async ( - api: any, - _wallet: any, - _point: number, + api: RollerRPCAPI, + wallet: any, + point: Ship, proxy: string, nonce: number, - urbitWallet: any + networkSeed: any, + breach?: boolean ) => { const from = { - ship: _point, //ship to configure keys + ship: point, //ship to configure keys proxy, }; - const networkSeed = await deriveNetworkSeedFromUrbitWallet(urbitWallet, 1); // TODO: do something here? if (Nothing.hasInstance(networkSeed)) { console.log("Network key Error: couldn't derive network keys"); @@ -102,21 +101,22 @@ export const configureKeys = async ( encrypt: addHexPrefix(pair.crypt.public), auth: addHexPrefix(pair.auth.public), cryptoSuite: String(CRYPTO_SUITE_VERSION), - breach: false, + breach: breach || false, }; const hash = await api.hashTransaction(nonce, from, 'configureKeys', data); - const sig = signTransactionHash(hash, _wallet.privateKey); - return api.configureKeys(sig, from, _wallet.address, data); + const sig = signTransactionHash(hash, wallet.privateKey); + return api.configureKeys(sig, from, wallet.address, data); }; -export const transferPoint = async ( - api: any, +export const transferPointRequest = async ( + api: RollerRPCAPI, _wallet: any, - _point: number, + _point: Ship, proxy: string, nonce: number, - address: string + address: string, + reset?: boolean ) => { const from = { ship: _point, // ship to transfer @@ -125,9 +125,8 @@ export const transferPoint = async ( const data = { address, - reset: false, + reset: reset || false, }; - const hash = await api.hashTransaction(nonce, from, 'transferPoint', data); const sig = signTransactionHash(hash, _wallet.privateKey); return api.transferPoint(sig, from, _wallet.address, data); @@ -146,8 +145,28 @@ const proxyType = (proxy: Proxy) => { } }; +const setProxy = async ( + api: RollerRPCAPI, + proxyAddressType: string, + sig: Signature, + from: From, + address: EthAddress, + data: AddressParams +) => { + switch (proxyAddressType) { + case 'manage': + return await api.setManagementProxy(sig, from, address, data); + case 'spawn': + return await api.setSpawnProxy(sig, from, address, data); + case 'transfer': + return await api.setTransferProxy(sig, from, address, data); + default: + throw new Error(`Unknown proxyType ${proxyAddressType}`); + } +}; + export const registerProxyAddress = async ( - api: any, + api: RollerRPCAPI, _wallet: any, _point: Ship, proxy: string, @@ -159,42 +178,22 @@ export const registerProxyAddress = async ( ship: _point, proxy, }; - const managementData = { address }; - const managementHash = await api.hashTransaction( + const proxyData = { address }; + const proxyHash = await api.hashTransaction( nonce, from, proxyType(proxyAddressType), - managementData + proxyData ); + return setProxy( api, proxyAddressType, - signTransactionHash(managementHash, _wallet.privateKey), + signTransactionHash(proxyHash, _wallet.privateKey), from, _wallet.address, - managementData + proxyData ); }; -const setProxy = async ( - api: any, - proxyAddressType: string, - sig: Signature, - from: From, - address: EthAddress, - data: AddressParams -) => { - switch (proxyAddressType) { - case 'manage': - return await api.setManagementProxy(sig, from, address, data); - case 'spawn': - return await api.setSpawnProxy(sig, from, address, data); - case 'transfer': - return await api.setTrasferProxy(sig, from, address, data); - default: - throw new Error(`Unknown proxyType ${proxyAddressType}`); - } -}; - -export const hasPoint = (point: number) => (invite: Invite) => - invite.planet === point; +export const reticketL2Point = async () => {}; diff --git a/src/store/roller.ts b/src/store/roller.ts index 9306cab04..7361349b2 100644 --- a/src/store/roller.ts +++ b/src/store/roller.ts @@ -1,8 +1,7 @@ import create from 'zustand'; -import { L2Point, PendingTransaction, Ownership } from '@urbit/roller-api'; +import { L2Point, PendingTransaction } from '@urbit/roller-api'; import { HOUR, isL2, isL2Spawn } from 'lib/utils/roller'; -import { increaseProxyNonce, setProxyNonce } from 'lib/utils/nonce'; import { Invite } from 'lib/types/Invite'; export interface RollerStore { @@ -12,17 +11,6 @@ export interface RollerStore { currentPoint: L2Point | null; currentL2: boolean; currentL2Spawn: boolean; - nonces: { - [point: number]: Ownership; - }; - increaseNonce: (point: number, proxy: string) => void; - setNonce: ( - point: number, - owner: Ownership, - proxy: string, - nonce: number - ) => void; - setNonces: (point: number, owner: Ownership) => void; invites: Invite[]; recentlyCompleted: number; setNextBatchTime: (nextBatchTime: number) => void; @@ -41,27 +29,6 @@ export const useRollerStore = create(set => ({ currentL2Spawn: false, invites: [], recentlyCompleted: 0, - nonces: {}, - increaseNonce: (point: number, proxy: string) => - set(state => { - const owner = increaseProxyNonce(state.nonces[point], proxy); - console.log(point, proxy, owner); - if (!owner) throw new Error("Can't increase nonce for this proxy"); - return { ...state, nonces: { ...state.nonces, [point]: owner } }; - }), - setNonce: (point: number, owner: Ownership, proxy: string, nonce: number) => - set(state => { - const updatedOwner = setProxyNonce(owner, proxy, nonce); - if (!updatedOwner) throw new Error("Can't set nonce for this proxy"); - return { - ...state, - nonces: { ...state.nonces, [point]: updatedOwner }, - }; - }), - setNonces: (point: number, owner: Ownership) => - set(state => { - return { ...state, nonces: { ...state.nonces, [point]: owner } }; - }), setNextBatchTime: (nextBatchTime: number) => set(() => ({ nextBatchTime })), setNextRoll: (nextRoll: string) => set(() => ({ nextRoll })), setPendingTransactions: (pendingTransactions: PendingTransaction[]) => diff --git a/src/views/Activate/MasterKeyTransfer.tsx b/src/views/Activate/MasterKeyTransfer.tsx index c4aafc0cd..f62936ae2 100644 --- a/src/views/Activate/MasterKeyTransfer.tsx +++ b/src/views/Activate/MasterKeyTransfer.tsx @@ -14,7 +14,7 @@ import DangerBox from './DangerBox'; import View from 'components/View'; const MasterKeyTransfer = () => { - const { acceptInvite } = useRoller(); + const { performL2Reticket } = useRoller(); const { derivedPatp, derivedPoint, @@ -27,7 +27,7 @@ const MasterKeyTransfer = () => { setError(undefined); try { - await acceptInvite({ + await performL2Reticket({ point: derivedPoint.value, to: derivedWallet.value.ownership.keys.address, manager: derivedWallet.value.management.keys.address, @@ -38,7 +38,12 @@ const MasterKeyTransfer = () => { console.error(error); setError(error); } - }, [derivedWallet, acceptInvite, derivedPoint.value, inviteWallet.value]); + }, [ + derivedWallet, + performL2Reticket, + derivedPoint.value, + inviteWallet.value, + ]); useFadeIn(); diff --git a/src/views/Point/Point.tsx b/src/views/Point/Point.tsx index e3ee3ac61..42b453fc7 100644 --- a/src/views/Point/Point.tsx +++ b/src/views/Point/Point.tsx @@ -45,8 +45,6 @@ export default function Point() { nextRoll, invites, setCurrentPoint, - setNonces, - increaseNonce, } = useRollerStore(); const networkKeysSet = useHasNetworkKeysSet(); const [showModal, setShowModal] = useState(false); @@ -64,23 +62,16 @@ export default function Point() { const loadL2Info = useCallback(async () => { const getTransactions = async () => { const pointInfo = await api.getPoint(Number(point)); - const pendingTxs = await api.getPendingByShip(Number(point)); + if (isDevelopment) { console.log('POINT INFO', pointInfo); } - setNonces(point, pointInfo.ownership); - - for (let index = 0; index < pendingTxs.length; index++) { - const proxy = pendingTxs[index].rawTx?.from?.proxy; - console.log(pointInfo.ownership, proxy); - increaseNonce(point, proxy); - } setCurrentPoint(pointInfo); await getInvites(isL2(pointInfo.dominion)); }; await getTransactions(); - }, [api, point, setCurrentPoint, increaseNonce, getInvites, setNonces]); + }, [api, point, getInvites, setCurrentPoint]); useEffect(() => { loadL1Info(); @@ -148,7 +139,6 @@ export default function Point() { ); })(); - const address = need.addressFromWallet(wallet); const spawnedPending = pendingTransactions.filter( ({ rawTx }) => rawTx?.tx?.type === 'spawn' @@ -197,6 +187,12 @@ export default function Point() { ? 'Management Address Changed' : pendingTx.rawTx?.tx?.type === 'configure-keys' ? 'Network Keys Configured' + : pendingTx.rawTx?.tx?.type === 'transfer-point' + ? 'Point Transfered' + : pendingTx.rawTx?.tx?.type === 'set-transfer-proxy' + ? 'Transfer Proxy Changed' + : pendingTx.rawTx?.tx?.type === 'set-spawn-proxy' + ? 'Spawn Proxy Changed' : ''}
diff --git a/src/views/UrbitID/ResetKeys/ResetExecute.js b/src/views/UrbitID/ResetKeys/ResetExecute.js index 513a4ceee..d3229fe75 100644 --- a/src/views/UrbitID/ResetKeys/ResetExecute.js +++ b/src/views/UrbitID/ResetKeys/ResetExecute.js @@ -16,7 +16,9 @@ import { useWallet } from 'store/wallet'; import { usePointCursor } from 'store/pointCursor'; import { usePointCache } from 'store/pointCache'; import { useHistory } from 'store/history'; +import { useRollerStore } from 'store/roller'; +import useRoller from 'lib/useRoller'; import { useWalletConnect } from 'lib/useWalletConnect'; import { RestartButton, ForwardButton } from 'components/Buttons'; @@ -53,6 +55,8 @@ export default function ResetExecute({ newWallet, setNewWallet }) { walletType, walletHdPath, } = useWallet(); + const { performL2Reticket } = useRoller(); + const { currentL2 } = useRollerStore(); const { pointCursor } = usePointCursor(); const { getDetails } = usePointCache(); const { @@ -92,20 +96,33 @@ export default function ResetExecute({ newWallet, setNewWallet }) { walletType === WALLET_TYPES.WALLET_CONNECT ? wcSend : undefined; try { - await reticketPointBetweenWallets({ - fromWallet: need.wallet(wallet), - fromWalletType: walletType, - fromWalletHdPath: walletHdPath, - toWallet: newWallet.value.wallet, - point: point, - web3: need.web3(web3), - contracts: need.contracts(contracts), - networkType, - onUpdate: handleUpdate, - nextRevision: networkRevision + 1, - txnSigner, - txnSender, - }); + if (currentL2) { + console.log(newWallet.value); + // FIXME: the useEffect is called twice, which is fine since the duplicate + // L2 txs will be discarded + // + await performL2Reticket({ + point, + to: newWallet.value.wallet.ownership.keys.address, + manager: newWallet.value.wallet.management.keys.address, + toWallet: newWallet.value.wallet, + fromWallet: need.wallet(wallet), + }); + } else + await reticketPointBetweenWallets({ + fromWallet: need.wallet(wallet), + fromWalletType: walletType, + fromWalletHdPath: walletHdPath, + toWallet: newWallet.value.wallet, + point: point, + web3: need.web3(web3), + contracts: need.contracts(contracts), + networkType, + onUpdate: handleUpdate, + nextRevision: networkRevision + 1, + txnSigner, + txnSender, + }); } catch (err) { console.error(err); setGeneralError(err); diff --git a/src/views/UrbitID/SetProxy.js b/src/views/UrbitID/SetProxy.js index e38d874ee..08cff528f 100644 --- a/src/views/UrbitID/SetProxy.js +++ b/src/views/UrbitID/SetProxy.js @@ -161,7 +161,6 @@ export default function SetProxy() { ]); const validateForm = useCallback((values, errors) => { - console.log('hola'); if (!values.unset && errors.address) { return errors; } diff --git a/src/views/UrbitID/Transfer.js b/src/views/UrbitID/Transfer.js index bbc2e9db1..135c58e25 100644 --- a/src/views/UrbitID/Transfer.js +++ b/src/views/UrbitID/Transfer.js @@ -1,18 +1,20 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import cn from 'classnames'; -import { Grid, Text } from 'indigo-react'; +import { Grid, Text, Button } from 'indigo-react'; import * as azimuth from 'azimuth-js'; import { useNetwork } from 'store/network'; import { usePointCursor } from 'store/pointCursor'; import { usePointCache } from 'store/pointCache'; import { useStarReleaseCache } from 'store/starRelease'; +import { useRollerStore } from 'store/roller'; import * as need from 'lib/need'; import { useLocalRouter } from 'lib/LocalRouter'; import useCurrentPointName from 'lib/useCurrentPointName'; import useEthereumTransaction from 'lib/useEthereumTransaction'; import { GAS_LIMITS } from 'lib/constants'; +import useRoller from 'lib/useRoller'; import ViewHeader from 'components/ViewHeader'; import NoticeBox from 'components/NoticeBox'; @@ -45,6 +47,9 @@ export default function AdminTransfer() { const name = useCurrentPointName(); const { pointCursor } = usePointCursor(); const { starReleaseDetails } = useStarReleaseCache(); + const { currentL2 } = useRollerStore(); + const { setProxyAddress, getPendingTransactions } = useRoller(); + const [owner, setNewOwner] = useState(''); const { construct, @@ -54,29 +59,46 @@ export default function AdminTransfer() { bind, } = useTransfer(); + const point = need.point(pointCursor); + const needLockupWarning = useMemo(() => { - const point = need.point(pointCursor); const az = azimuth.azimuth; return ( az.getPointSize(point) === az.PointSize.Galaxy && starReleaseDetails.map(a => a.kind).getOrElse('none') !== 'none' ); - }, [pointCursor, starReleaseDetails]); + }, [starReleaseDetails, point]); const validate = useMemo( () => composeValidator({ address: buildAddressValidator() }), [] ); + const onSignTx = useCallback(async () => { + // setLoading(true); + if (owner === '') return; + try { + await setProxyAddress('transfer', owner); + getPendingTransactions(point); + pop(); + } catch (error) { + // setError(error); + } finally { + // setLoading(false); + } + }, [owner, getPendingTransactions, point, pop, setProxyAddress]); + const onValues = useCallback( ({ valid, values }) => { if (valid) { construct(values.address); + setNewOwner(values.address); } else { unconstruct(); + setNewOwner(''); } }, - [construct, unconstruct] + [construct, unconstruct, setNewOwner] ); return ( @@ -127,12 +149,25 @@ export default function AdminTransfer() { - pop()} - /> + {currentL2 ? ( + + {'Sign Transaction'} + + ) : ( + pop()} + /> + )} )} diff --git a/src/views/UrbitOS/Home.tsx b/src/views/UrbitOS/Home.tsx index 167ce6248..f011a3ea8 100644 --- a/src/views/UrbitOS/Home.tsx +++ b/src/views/UrbitOS/Home.tsx @@ -12,7 +12,7 @@ import { ForwardButton } from 'components/Buttons'; import NetworkingKeys from 'components/NetworkingKeys'; import { useLocalRouter } from 'lib/LocalRouter'; -import { L1Point } from 'types/L1Point'; +import { L1Point } from 'lib/types/L1Point'; import AlertBox from 'components/AlertBox'; import DownloadKeyfileButton from 'components/DownloadKeyfileButton'; import useKeyfileGenerator from 'lib/useKeyfileGenerator'; diff --git a/src/views/UrbitOS/NetworkingKeys.js b/src/views/UrbitOS/NetworkingKeys.js index 8f198f19c..48c20dc93 100644 --- a/src/views/UrbitOS/NetworkingKeys.js +++ b/src/views/UrbitOS/NetworkingKeys.js @@ -1,6 +1,6 @@ -import React, { useCallback, useMemo, useRef } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { Just } from 'folktale/maybe'; -import { Grid, ToggleInput, CheckboxInput } from 'indigo-react'; +import { Grid, ToggleInput, CheckboxInput, Button } from 'indigo-react'; import * as azimuth from 'azimuth-js'; import { randomHex } from 'web3-utils'; @@ -8,7 +8,9 @@ import { usePointCursor } from 'store/pointCursor'; import { usePointCache } from 'store/pointCache'; import { useNetwork } from 'store/network'; import { useWallet } from 'store/wallet'; +import { useRollerStore } from 'store/roller'; +import useRoller from 'lib/useRoller'; import { useLocalRouter } from 'lib/LocalRouter'; import * as need from 'lib/need'; import { @@ -141,6 +143,9 @@ export default function UrbitOSNetworkingKeys({ const { pop } = useLocalRouter(); const { pointCursor } = usePointCursor(); const { getDetails } = usePointCache(); + const { currentL2 } = useRollerStore(); + const { configureNetworkingKeys, getPendingTransactions } = useRoller(); + const [breach, setBreach] = useState(false); const point = need.point(pointCursor); const details = getDetails(point); @@ -187,17 +192,53 @@ export default function UrbitOSNetworkingKeys({ values.useNetworkSeed ? values.networkSeed : undefined, values.useDiscontinuity ); + setBreach(values.useDiscontinuity); + setManualNetworkSeed(values.useNetworkSeed); } else { unconstruct(); } if (!values.useNetworkSeed && values.networkSeed) { form.change('networkSeed', ''); + setBreach(false); + setManualNetworkSeed(''); } }, - [construct, unconstruct] + [construct, unconstruct, setManualNetworkSeed, setBreach] ); + const setNetworkingKeys = useCallback(async () => { + // setLoading(true); + console.log(breach, manualNetworkSeed); + try { + const txHash = await configureNetworkingKeys({ + breach, + manualNetworkSeed, + }); + // TODO: just use the tx hash instead? + getPendingTransactions(point); + // TODO: this is just so we get visual feedback that the tx has gone through + // but this should be addressed with the new UI flow (-> download keyfile) + // + // A question here is how to deal with the modals/messages about the keys not being + // set since they are in pending in the Roller... + // we could inspect if there's a changeKeys in the local list of pending txs,... + // + pop(); + } catch (error) { + // setError(error); + } finally { + // setLoading(false); + } + }, [ + breach, + manualNetworkSeed, + getPendingTransactions, + point, + pop, + configureNetworkingKeys, + ]); + const initialValues = useMemo( () => ({ useNetworkSeed: false, @@ -280,13 +321,25 @@ export default function UrbitOSNetworkingKeys({ )} - pop()} - /> + {currentL2 ? ( + + {'Sign Transaction'} + + ) : ( + pop()} + /> + )} )}