├── .npmignore ├── .gitignore ├── .travis.yml ├── .eslintrc.json ├── utils.js ├── internal ├── genTransaction.js ├── claims.js ├── delegatedSending.js ├── linearSR.js ├── polls.js ├── conditionalSR.js ├── ecliptic.js └── azimuth.js ├── resources ├── chainDetails.json └── reasons.json ├── index.js ├── LICENSE ├── claims.js ├── package.json ├── txn.js ├── linearSR.js ├── CHANGELOG ├── polls.js ├── contracts.js ├── conditionalSR.js ├── delegatedSending.js ├── README.md ├── ecliptic.js ├── azimuth.js ├── test └── test.js └── check.js /.npmignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | .travis.yml 3 | .eslintrc.json 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | node_modules/ 4 | *.swp 5 | *.swo 6 | ref/ 7 | tags 8 | .nyc_output 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10' 4 | cache: 5 | directories: 6 | - "node_modules" 7 | 8 | script: 9 | - npm run lint 10 | - npm run test 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "globals": { 9 | "Atomics": "readonly", 10 | "SharedArrayBuffer": "readonly" 11 | }, 12 | "parserOptions": { 13 | "ecmaVersion": 2018 14 | }, 15 | "rules": { 16 | } 17 | } -------------------------------------------------------------------------------- /utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Utilities 3 | * @module utils 4 | */ 5 | 6 | const ethUtil = require('ethereumjs-util'); 7 | 8 | /** 9 | * Check if two addresses are equal. 10 | * @param {String} a1 - an Ethereum address. 11 | * @param {String} a2 - an Ethereum address. 12 | * @return {Bool} True if the addresses are equal, false otherwise. 13 | */ 14 | function addressEquals(a1, a2) { 15 | return (ethUtil.toChecksumAddress(a1) === ethUtil.toChecksumAddress(a2)); 16 | } 17 | 18 | module.exports = ethUtil; 19 | module.exports.addressEquals = addressEquals; 20 | 21 | -------------------------------------------------------------------------------- /internal/genTransaction.js: -------------------------------------------------------------------------------- 1 | 2 | // simple unsigned transaction generator 3 | // 4 | // grab the specified contract method, call it with the passed arguments (if 5 | // there are any), and assemble a raw unsigned transaction. 6 | // 7 | function tx(contract, method, ...args) { 8 | let func = contract.methods[method] 9 | 10 | let called = 11 | args === null 12 | ? func.call() 13 | : func.apply(this, args) 14 | 15 | let data = called.encodeABI() 16 | 17 | return { to: contract._address, data: data, value: 0 } 18 | } 19 | 20 | module.exports = { 21 | tx 22 | } 23 | -------------------------------------------------------------------------------- /resources/chainDetails.json: -------------------------------------------------------------------------------- 1 | { 2 | "mainnet": { 3 | "azimuth": { 4 | "address": "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB", 5 | "genesis": 6784880 6 | } 7 | }, 8 | "rinkeby": { 9 | "azimuth": { 10 | "address": "0x2BAfc1419F41fB850941b8412eC94723489ff41D", 11 | "genesis": 7440691 12 | } 13 | }, 14 | "ropsten": { 15 | "azimuth": { 16 | "address": "0x308ab6a6024cf198B57e008d0Ac9Ad0219886579", 17 | "genesis": 4601630 18 | } 19 | }, 20 | "local": { 21 | "azimuth": { 22 | "address": "0x863d9c2e5c4c133596cfac29d55255f0d0f86381", 23 | "genesis": 0 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /internal/claims.js: -------------------------------------------------------------------------------- 1 | const gen = require("./genTransaction"); 2 | 3 | module.exports.addClaim = (contracts, _point, _protocol, _claim, _dossier) => 4 | gen.tx(contracts.claims, "addClaim", _point, _protocol, _claim, _dossier); 5 | 6 | module.exports.removeClaim = (contracts, _point, _protocol, _claim) => 7 | gen.tx(contracts.claims, "removeClaim", _point, _protocol, _claim); 8 | 9 | 10 | module.exports.getClaim = (contracts, whose, index) => 11 | contracts.claims.methods.claims(whose, index).call(); 12 | 13 | module.exports.getAllClaims = (contracts, whose) => 14 | Promise.all( 15 | [...Array(16).keys()].map((i) => 16 | contracts.claims.methods.claims(whose, i).call() 17 | ) 18 | ).then((claims) => 19 | claims.filter( 20 | (claim) => 21 | claim.protocol !== "" || claim.claim !== "" 22 | ) 23 | ); 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * azimuth-js 3 | * @module index 4 | */ 5 | 6 | 'use strict'; 7 | 8 | const check = require('./check'); 9 | const conditionalSR = require('./conditionalSR'); 10 | const ecliptic = require('./ecliptic'); 11 | const contracts = require('./contracts'); 12 | const linearSR = require('./linearSR'); 13 | const polls = require('./polls'); 14 | const azimuth = require('./azimuth'); 15 | const delegatedSending = require('./delegatedSending'); 16 | const txn = require('./txn'); 17 | const utils = require('./utils'); 18 | const chainDetails = require('./resources/chainDetails.json'); 19 | const claims = require('./claims'); 20 | 21 | const initContracts = contracts.initContracts; 22 | const initContractsPartial = contracts.initContractsPartial; 23 | 24 | module.exports = { 25 | check, 26 | conditionalSR, 27 | ecliptic, 28 | linearSR, 29 | polls, 30 | claims, 31 | azimuth, 32 | delegatedSending, 33 | txn, 34 | utils, 35 | contracts, 36 | initContracts, 37 | initContractsPartial, 38 | chainDetails 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Urbit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /internal/delegatedSending.js: -------------------------------------------------------------------------------- 1 | 2 | const gen = require('./genTransaction') 3 | 4 | module.exports.pools = (contracts, pool, prefix) => 5 | contracts.delegatedSending.methods.pools(pool, prefix).call() 6 | 7 | module.exports.getInvited = (contracts, point) => 8 | contracts.delegatedSending.methods.getInvited(point).call() 9 | 10 | module.exports.invitedBy = (contracts, point) => 11 | contracts.delegatedSending.methods.invitedBy(point).call() 12 | 13 | module.exports.canSend = (contracts, as, point) => 14 | contracts.delegatedSending.methods.canSend(as, point).call() 15 | 16 | module.exports.getPool = (contracts, point) => 17 | contracts.delegatedSending.methods.getPool(point).call() 18 | 19 | module.exports.getPoolStars = (contracts, pool) => 20 | contracts.delegatedSending.methods.getPoolStars(pool).call() 21 | 22 | module.exports.canReceive = (contracts, recipient) => 23 | contracts.delegatedSending.methods.canReceive(recipient).call() 24 | 25 | module.exports.setPoolSize = (contracts, _as, _for, _size) => 26 | gen.tx(contracts.delegatedSending, 'setPoolSize', _as, _for, _size) 27 | 28 | module.exports.sendPoint = (contracts, _as, _point, _to) => 29 | gen.tx(contracts.delegatedSending, 'sendPoint', _as, _point, _to) 30 | 31 | -------------------------------------------------------------------------------- /internal/linearSR.js: -------------------------------------------------------------------------------- 1 | 2 | const gen = require('./genTransaction') 3 | 4 | module.exports.getBatch = (contracts, address) => 5 | contracts.linearSR.methods.batches(address).call() 6 | 7 | module.exports.getRemainingStars = (contracts, address) => 8 | contracts.linearSR.methods.getRemainingStars(address).call() 9 | 10 | module.exports.verifyBalance = (contracts, address) => 11 | contracts.linearSR.methods.verifyBalance(address).call() 12 | 13 | module.exports.getStartTime = (contracts) => 14 | contracts.linearSR.methods.start().call() 15 | 16 | module.exports.getWithdrawLimit = (contracts, address) => 17 | contracts.linearSR.methods.withdrawLimit(address).call() 18 | 19 | module.exports.getApprovedTransfer = (contracts, address) => 20 | contracts.linearSR.methods.transfers(address).call() 21 | 22 | module.exports.approveBatchTransfer = (contracts, _to) => 23 | gen.tx(contracts.linearSR, 'approveBatchTransfer', _to) 24 | 25 | module.exports.transferBatch = (contracts, _from) => 26 | gen.tx(contracts.linearSR, 'transferBatch', _from) 27 | 28 | module.exports.withdraw = (contracts) => 29 | gen.tx(contracts.linearSR, 'withdraw') 30 | 31 | module.exports.withdrawTo = (contracts, _to) => 32 | gen.tx(contracts.linearSR, 'withdraw', _to) 33 | 34 | -------------------------------------------------------------------------------- /resources/reasons.json: -------------------------------------------------------------------------------- 1 | { 2 | "permission": "Insufficient permission for this action", 3 | "zero": "Cannot target the zero address", 4 | "inactive": "Point is not active", 5 | "spawned": "Point already spawned", 6 | "spawnSize": "Spawning galaxy planets is not supported", 7 | "spawnPrefix": "Prefix is unable to spawn children", 8 | "sponsorBoot": "Sponsor has not been booted", 9 | "sponsor": "Invalid sponsor for point", 10 | "sponsorless": "Point has no sponsor", 11 | "notEscape": "Point is not escaping", 12 | "notGalaxy": "Only galaxies may vote", 13 | "upgradePath": "Unexpected upgrade path", 14 | "majority": "Proposal has already achieved majority", 15 | "pollTime": "Existing poll is still active or cooling down", 16 | "pollInactive": "Poll is not currently active", 17 | "pollVoted": "Point already voted on this poll", 18 | "contractCant": "Contract has not been granted sufficient permission", 19 | "noRemaining": "No stars remaining in the contract", 20 | "withdrawLimit":"Withdraw limit reached", 21 | "forfeitLimit": "Remaining stars have all been forfeited", 22 | "hasForfeited": "Commitment has already forfeited", 23 | "notMissed": "Deadline has not been missed", 24 | "notApproved": "Not approved for transfer", 25 | "cantReceive": "Target not eligible to receive", 26 | "empty": "" 27 | } 28 | -------------------------------------------------------------------------------- /claims.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Claims API 3 | * @module claims 4 | */ 5 | 6 | const internal = require('./internal/claims'); 7 | 8 | /* 9 | * Writes a claim to the Claims contract. 10 | * @param {Object} contracts - An Urbit contracts object. 11 | * @param {Number} point - Point number. 12 | * @param {String} protocol - Context or platform of the claim. 13 | * @param {String} claim - The claim. 14 | * @param {String} dossier - Data relating to the claim, as proof. 15 | * @return {Object} An unsigned transaction object. 16 | */ 17 | module.exports.addClaim = internal.addClaim; 18 | 19 | /* 20 | * Removes a claim from the Claims contract. 21 | * @param {Object} contracts - An Urbit contracts object. 22 | * @param {Number} point - Point number. 23 | * @param {String} protocol - Context or platform of the claim. 24 | * @param {String} claim - The claim. 25 | * @return {Object} An unsigned transaction object. 26 | */ 27 | module.exports.removeClaim = internal.removeClaim; 28 | 29 | /* 30 | * Reads a claim of a point at a specific index. 31 | * @param {Object} contracts - An Urbit contracts object. 32 | * @param {Number} point - Point number. 33 | * @param {Number} index - The index of the array. 34 | * @return {Promise} A claims object. 35 | */ 36 | module.exports.getClaim = internal.getClaim; 37 | 38 | /* 39 | * Reads a claim of a point at a specific index. 40 | * @param {Object} contracts - An Urbit contracts object. 41 | * @param {Number} point - Point number. 42 | * @return {Promise} An array of claims objects. 43 | */ 44 | module.exports.getAllClaims = internal.getAllClaims; 45 | -------------------------------------------------------------------------------- /internal/polls.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports.getPollDuration = (contracts) => 3 | contracts.polls.methods.pollDuration().call() 4 | 5 | module.exports.getPollCooldown = (contracts) => 6 | contracts.polls.methods.pollCooldown().call() 7 | 8 | module.exports.getTotalVoters = (contracts) => 9 | contracts.polls.methods.totalVoters().call() 10 | 11 | module.exports.getDocumentProposals = (contracts) => 12 | contracts.polls.methods.getDocumentProposals().call() 13 | 14 | module.exports.getUpgradeProposals = (contracts) => 15 | contracts.polls.methods.getUpgradeProposals().call() 16 | 17 | module.exports.getDocumentMajorities = (contracts) => 18 | contracts.polls.methods.getDocumentMajorities().call() 19 | 20 | module.exports.getUpgradePoll = (contracts, proposal) => 21 | contracts.polls.methods.upgradePolls(proposal).call() 22 | 23 | module.exports.getDocumentPoll = (contracts, proposal) => 24 | contracts.polls.methods.documentPolls(proposal).call() 25 | 26 | module.exports.eclipticHasAchievedMajority = (contracts, proposal) => 27 | contracts.polls.methods.eclipticHasAchievedMajority(proposal).call() 28 | 29 | module.exports.documentHasAchievedMajority = (contracts, proposal) => 30 | contracts.polls.methods.documentHasAchievedMajority(proposal).call() 31 | 32 | module.exports.hasVotedOnUpgradePoll = (contracts, galaxy, proposal) => 33 | contracts.polls.methods.hasVotedOnUpgradePoll(galaxy, proposal).call() 34 | 35 | module.exports.hasVotedOnDocumentPoll = (contracts, galaxy, proposal) => 36 | contracts.polls.methods.hasVotedOnDocumentPoll(galaxy, proposal).call() 37 | 38 | module.exports.updateDocumentPoll = (contracts, proposal) => 39 | contracts.polls.methods.updateDocumentPoll(proposal).call() 40 | 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azimuth-js", 3 | "version": "0.23.0", 4 | "description": "Functions for interacting with Azimuth", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/urbit/azimuth-js.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test:deploy": "cd node_modules/azimuth-solidity && truffle deploy", 12 | "test:ganache": "ganache-cli --networkId 1 -m 'benefit crew supreme gesture quantum web media hazard theory mercy wing kitten' > /dev/null &", 13 | "test:setup": "npm run test:ganache && npm run test:deploy", 14 | "test:mocha": "nyc mocha --reporter spec --timeout 5000 --exit", 15 | "test:cleanup": "pkill -f ganache-cli", 16 | "test": "npm-run-all test:setup test:mocha test:cleanup --continue-on-error", 17 | "docs": "jsdoc2md *.js > docs/api.md", 18 | "prepublishOnly": "npm run docs", 19 | "lint": "eslint *.js" 20 | }, 21 | "keywords": [ 22 | "azimuth", 23 | "ecliptic", 24 | "smart", 25 | "contract" 26 | ], 27 | "devDependencies": { 28 | "bip39": "^3.0.4", 29 | "chai": "^4.3.4", 30 | "eslint": "^8.2.0", 31 | "ganache-cli": "^6.12.2", 32 | "hdkey": "2.0.1", 33 | "jsdoc": "^3.6.10", 34 | "jsdoc-to-markdown": "^7.1.0", 35 | "mocha": "^9.2.0", 36 | "npm-run-all": "^4.1.5", 37 | "nyc": "^15.1.0", 38 | "secp256k1": "^4.0.2", 39 | "truffle": "^5.4.30", 40 | "web3": "^1.3.6" 41 | }, 42 | "dependencies": { 43 | "azimuth-solidity": "^1.2.3", 44 | "ethereumjs-util": "^7.1.3", 45 | "urbit-ob": "^5.0.1" 46 | }, 47 | "peerDependencies": { 48 | "web3": "^1.3.6" 49 | }, 50 | "author": "Tlon", 51 | "license": "MIT", 52 | "bugs": { 53 | "url": "https://github.com/urbit/azimuth-js/issues" 54 | }, 55 | "homepage": "https://github.com/urbit/azimuth-js", 56 | "directories": { 57 | "test": "test" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /internal/conditionalSR.js: -------------------------------------------------------------------------------- 1 | 2 | const gen = require('./genTransaction') 3 | 4 | module.exports.getCommitment = (contracts, address) => 5 | contracts.conditionalSR.methods.commitments(address).call() 6 | 7 | module.exports.getRemainingStars = (contracts, address) => 8 | contracts.conditionalSR.methods.getRemainingStars(address).call() 9 | 10 | module.exports.getBatches = (contracts, address) => 11 | contracts.conditionalSR.methods.getBatches(address).call() 12 | 13 | module.exports.verifyBalance = (contracts, address) => 14 | contracts.conditionalSR.methods.verifyBalance(address).call() 15 | 16 | module.exports.getWithdrawLimit = (contracts, address, _batch) => 17 | contracts.conditionalSR.methods.withdrawLimit(address, _batch).call() 18 | 19 | module.exports.getApprovedTransfer = (contracts, address) => 20 | contracts.conditionalSR.methods.transfers(address).call() 21 | 22 | module.exports.getConditionsState = (contracts) => 23 | contracts.conditionalSR.methods.getConditionsState().call() 24 | 25 | module.exports.getWithdrawn = (contracts, address) => 26 | contracts.conditionalSR.methods.getWithdrawn(address).call(); 27 | 28 | module.exports.getForfeited = (contracts, address) => 29 | contracts.conditionalSR.methods.getForfeited(address).call() 30 | 31 | module.exports.approveCommitmentTransfer = (contracts, _to) => 32 | gen.tx(contracts.conditionalSR, 'approveCommitmentTransfer', _to) 33 | 34 | module.exports.transferCommitment = (contracts, _from) => 35 | gen.tx(contracts.conditionalSR, 'transferCommitment', _from) 36 | 37 | module.exports.withdraw = (contracts, _batch) => 38 | gen.tx(contracts.conditionalSR, 'withdrawToSelf', _batch) 39 | 40 | module.exports.withdrawTo = (contracts, _batch, _to) => 41 | gen.tx(contracts.conditionalSR, 'withdraw', _batch, _to) 42 | 43 | module.exports.forfeit = (contracts, _batch) => 44 | gen.tx(contracts.conditionalSR, 'forfeit', _batch) 45 | 46 | module.exports.analyzeCondition = (contracts, _condition) => 47 | gen.tx(contracts.conditionalSR, 'analyzeCondition', _condition) 48 | 49 | -------------------------------------------------------------------------------- /txn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Transaction handling 3 | * @module txn 4 | */ 5 | 6 | const utils = require('./utils'); 7 | 8 | function renderAsHex(value) { 9 | return utils.addHexPrefix(value.toString('hex')); 10 | } 11 | 12 | /** 13 | * Sign an unsigned transaction with the provided private key. 14 | * 15 | * If `tx.gas` is undefined, it will be estimated. If `tx.gasPrice` is 16 | * undefined, a default is used. If `tx.nonce` is undefined, Web3 will 17 | * retrieve the next nonce. And if `tx.chainId` is undefined, Web3 fills it 18 | * in. 19 | * 20 | * Note that Web3 cannot fill in most of those blanks when not connected to 21 | * a functioning node (i.e. "offline mode"), so those will have to be filled 22 | * in by the UI or user prior to signing. 23 | * 24 | * @param {Web3} web3 - a web3 object. 25 | * @param {Object} tx - an unsigned transaction. 26 | * @param {Buffer} privateKey - a private key. 27 | * @return {Promise} A signed transaction object with `messageHash`, 28 | * `v`, `r`, `s`, and `rawTransaction` fields. 29 | */ 30 | async function signTransaction(web3, tx, privateKey) { 31 | if (!utils.isValidPrivate(privateKey)) { 32 | throw "Invalid key"; 33 | } 34 | if (!tx.gas) { 35 | let preliminary = await web3.eth.estimateGas(tx); 36 | tx.gas = Math.floor(preliminary * 2.1); 37 | } 38 | let pk = renderAsHex(privateKey); 39 | return web3.eth.accounts.signTransaction(tx, pk); 40 | } 41 | 42 | /** 43 | * Forward a signed transaction to the blockchain. 44 | * @param {Web3} web3 - a web3 object. 45 | * @param {Object} signedTx - a signed transaction. 46 | * @return {Promise} 47 | */ 48 | function sendSignedTransaction(web3, signedTx) { 49 | let stx = signedTx.rawTransaction; 50 | return new Promise((resolve, reject) => { 51 | web3.eth 52 | .sendSignedTransaction(stx) 53 | .once('confirmation', (n, receipt) => { 54 | if (receipt.status) { 55 | resolve(); 56 | } else { 57 | reject(); 58 | } 59 | }) 60 | .on('error', (e) => { 61 | // eslint-disable-next-line no-console 62 | console.error(e); 63 | reject(); 64 | }); 65 | }); 66 | } 67 | 68 | module.exports = { 69 | signTransaction, 70 | sendSignedTransaction 71 | } 72 | 73 | -------------------------------------------------------------------------------- /linearSR.js: -------------------------------------------------------------------------------- 1 | /*** 2 | * linearSR API 3 | * @module linearSR 4 | */ 5 | 6 | const internal = require('./internal/linearSR'); 7 | 8 | /** 9 | * Return the details of a batch. 10 | * @param {Object} contracts - An Urbit contracts object. 11 | * @param {String} address - The participant/registered address for the batch. 12 | * @return {Promise} A batch object, with windup, rate, rateUnit, 13 | * amount, withdrawn. 14 | */ 15 | module.exports.getBatch = internal.getBatch; 16 | 17 | /** 18 | * Return the list of stars that have been deposited into, but not yet 19 | * withdrawn from a batch. 20 | * @param {Object} contracts - An Urbit contracts object. 21 | * @param {String} address - The participant/registered address for the batch. 22 | * @return {Promise>} The stars left in the batch. 23 | */ 24 | module.exports.getRemainingStars = internal.getRemainingStars; 25 | 26 | /** 27 | * Return whether the amount of stars deposited into the batch checks out. 28 | * @param {Object} contracts - An Urbit contracts object. 29 | * @param {String} address - The participant/registered address for the batch. 30 | * @return {Promise} true if sufficient stars have been deposited. 31 | */ 32 | module.exports.verifyBalance = internal.verifyBalance; 33 | 34 | /** 35 | * Return the timestamp at which the release was started. 36 | * @param {Object} contracts - An Urbit contracts object. 37 | * @return {Promise} A timestamp. 38 | */ 39 | module.exports.getStartTime = internal.getStartTime; 40 | 41 | /** 42 | * Return the amount of stars a participant is allowed to withdraw from their 43 | * batch at the current time. 44 | * @param {Object} contracts - An Urbit contracts object. 45 | * @param {String} address - The participant/registered address for the batch. 46 | * @return {Promise} the withdraw limit. 47 | */ 48 | module.exports.getWithdrawLimit = internal.getWithdrawLimit; 49 | 50 | /** 51 | * Return the address this batch can be transferred to. 52 | * @param {Object} contracts - An Urbit contracts object. 53 | * @param {String} address - The participant/registered address for the batch. 54 | * @return {Promise} The approved transfer address, 0x0 for none. 55 | */ 56 | module.exports.getApprovedTransfer = internal.getApprovedTransfer; 57 | 58 | 59 | /** 60 | * Approve the transfer of a batch to another address. 61 | * @param {Object} contracts - An Urbit contracts object. 62 | * @param {String} address - The address to transfer to. 63 | * @return {Object} An unsigned transaction object. 64 | */ 65 | module.exports.approveBatchTransfer = internal.approveBatchTransfer; 66 | 67 | /** 68 | * Make an approved transfer of the specified batch to the caller's address. 69 | * @param {Object} contracts - An Urbit contracts object. 70 | * @param {String} address - The address to transfer from. 71 | * @return {Object} An unsigned transaction object. 72 | */ 73 | module.exports.transferBatch = internal.transferBatch; 74 | 75 | /** 76 | * Withdraw one star to the caller's address. 77 | * @param {Object} contracts - An Urbit contracts object. 78 | * @return {Object} An unsigned transaction object. 79 | */ 80 | module.exports.withdraw = internal.withdraw; 81 | 82 | /** 83 | * Withdraw one star to the specified address. 84 | * @param {Object} contracts - An Urbit contracts object. 85 | * @param {String} address - The address to withdraw to. 86 | * @return {Object} An unsigned transaction object. 87 | */ 88 | module.exports.withdrawTo = internal.withdrawTo; 89 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # Changelog 2 | - 0.23.0 (2022-02-08) 3 | * Adds a 'getManagementProxy' function, which was conspicuously missing. 4 | (#83) 5 | 6 | - 0.22.2 (2022-01-21) 7 | * Updated npm dependencies. (#79) 8 | * Fixed spawn functions. (#62, #80) 9 | 10 | - 0.22.1 (2021-05-25) 11 | * Changes the various 'newContract' functions to require a 'contracts' 12 | object as input, similar to the rest of the API. (#72) 13 | 14 | - 0.22.0 (2021-05-25) 15 | * Exposes the various individual contracts (Ecliptic, Azimuth, etc.) for 16 | creation. 17 | * Major version bumps to the bip39, secp256k1, and hdkey dependencies. 18 | * Miscellaneous minor/internal housekeeping. 19 | 20 | - 0.21.0 (2020-12-03) 21 | * Adds some facilities for working with claims: 22 | 23 | * Adds 'removeClaim' and 'getAllClaims' functions for removing and 24 | reading claims, respectively. (#64) 25 | 26 | * initContractsPartial now initialises the Claims contract. (#64) 27 | 28 | * Major version dependency bumps to ethereumjs-util and Truffle. 29 | 30 | * Patch version dependency bump to azimuth-solidity v1.2.2. 31 | 32 | - 0.20.1 (2020-06-29) 33 | * Major version dependency bumps to urbit-ob and mocha. 34 | 35 | - 0.20.0 (2020-05-16) 36 | * Exposes the Azimuth contract address on the Ethereum mainnet as 37 | 'azimuth.mainnet'. 38 | 39 | - 0.19.0 (2020-03-06) 40 | * Adds support for the 'getEscapeRequests' and 'getEscapeRequestsCount' 41 | methods of the Azimuth contract. 42 | * Adds initial support for the Claims contract with 'addClaim' and 43 | 'getClaim' functions. 44 | 45 | - 0.18.0 (2020-02-26) 46 | * Adds a missing hasBeenLinked check to the delegated sending logic. (#55) 47 | * Adds the 'getForfeited' binding to the conditionalSR contract. (#56) 48 | 49 | - 0.17.0 (2019-12-06) 50 | * Adds a number of additional function bindings to the polls contract. 51 | (#53, #54). 52 | 53 | - 0.16.0 (2019-11-07) 54 | * Adds the 'getWithdrawn' method to the conditional star release contract 55 | bindings. (#52) 56 | 57 | - 0.15.4 (2019-11-06) 58 | * Fixes several ABI mismatch bugs in the conditional star release contract 59 | bindings. (#51) 60 | 61 | - 0.15.3 (2019-11-01) 62 | * Adds the conditional star release contract to 'initContracts'. (#50) 63 | 64 | - 0.15.2 (2019-10-29) 65 | * Fixes a bug in the bindings to the 'withdraw' method of the linear star 66 | release contract. (#48) 67 | * Bumps the azimuth-solidity and urbit-ob dependency versions. 68 | 69 | - 0.15.1 (2019-07-22) 70 | * Miscellaneous fixes to support a more recent web3 version. Note that 71 | the library now supports web3 v1.0.0-beta.54 exclusively, rather than a 72 | range in the beta series. (#46) 73 | 74 | - 0.15.0 (2019-06-18) 75 | * Adds a utility function for retrieving a point's activation block. (#43) 76 | * Fixes a bug that prevented the polls tests from running. (#40) 77 | * Miscellaneous internal improvements and docs fixes. (#41, #42) 78 | 79 | - 0.14.2 (2019-06-06) 80 | * Fixes a bug where the linear and conditional star release modules 81 | weren't actually being imported in the "check" module. (#38) 82 | 83 | - 0.14.1 (2019-06-06) 84 | * Fixes an issue wherein 'getPlanetsToSend' could return incorrect 85 | results. (#37) 86 | 87 | - 0.14.0 (2019-05-31) 88 | * Adds support for the latest version of the delegated sending contract. 89 | (#31) 90 | 91 | - 0.13.2 (2019-04-26) 92 | * Fixes an off-by-one error in getUnspawnedChildren. 93 | 94 | - 0.13.1 (2019-04-25) 95 | * Minor bug fixes. 96 | 97 | - 0.13.0 (2019-04-24) 98 | * Adds a convenience function for grabbing the unspawned children of a 99 | point. 100 | 101 | - 0.12.0 (2019-04-19) 102 | * Make web3 a peer dependency. 103 | 104 | -------------------------------------------------------------------------------- /polls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Polls API 3 | * @module polls 4 | */ 5 | 6 | const internal = require('./internal/polls'); 7 | 8 | /* 9 | * Get the duration of polls. 10 | * @param {Object} contracts - An Urbit contracts object. 11 | * @return {Promise} Poll duration in seconds. 12 | */ 13 | module.exports.getPollDuration = internal.getPollDuration; 14 | 15 | /* 16 | * Get the cooldown period of polls. 17 | * @param {Object} contracts - An Urbit contracts object. 18 | * @return {Promise} Poll cooldown period in seconds. 19 | */ 20 | module.exports.getPollCooldown = internal.getPollCooldown; 21 | 22 | /* 23 | * Get the amount of galaxies eligible to vote. 24 | * @param {Object} contracts - An Urbit contracts object. 25 | * @return {Promise} Voting galaxies. 26 | */ 27 | module.exports.getTotalVoters = internal.getTotalVoters; 28 | 29 | /* 30 | * Get all documents that have ever been proposed. 31 | * @param {Object} contracts - An Urbit contracts object. 32 | * @return {Promise>} Document hashes. 33 | */ 34 | module.exports.getDocumentProposals = internal.getDocumentProposals; 35 | 36 | /* 37 | * Get all upgrades that have ever been proposed. 38 | * @param {Object} contracts - An Urbit contracts object. 39 | * @return {Promise>} Contract addresses. 40 | */ 41 | module.exports.getUpgradeProposals = internal.getUpgradeProposals; 42 | 43 | /* 44 | * Get all documents that have achieved majority. 45 | * @param {Object} contracts - An Urbit contracts object. 46 | * @return {Promise>} Document hashes. 47 | */ 48 | module.exports.getDocumentMajorities = internal.getDocumentMajorities; 49 | 50 | /* 51 | * Get the upgrade poll at the target address. 52 | * @param {Object} contracts - An Urbit contracts object. 53 | * @param {String} proposal - The target proposal address. 54 | * @return {Promise} The specified poll. 55 | */ 56 | module.exports.getUpgradePoll = internal.getUpgradePoll; 57 | 58 | /* 59 | * Get the document poll at the target address. 60 | * @param {Object} contracts - An Urbit contracts object. 61 | * @param {String} proposal - The target proposal address. 62 | * @return {Promise} The specified poll. 63 | */ 64 | module.exports.getDocumentPoll = internal.getDocumentPoll; 65 | 66 | /* 67 | * Check if a ecliptic proposal has achieved a majority. 68 | * @param {Object} contracts - An Urbit contracts object. 69 | * @param {String} proposal - The target proposal address. 70 | * @return {Promise} True if majority achieved, false otherwise. 71 | */ 72 | module.exports.eclipticHasAchievedMajority = internal.eclipticHasAchievedMajority; 73 | 74 | /* 75 | * Check if a document proposal has achieved a majority. 76 | * @param {Object} contracts - An Urbit contracts object. 77 | * @param {String} proposal - The target proposal address. 78 | * @return {Promise} True if majority achieved, false otherwise. 79 | */ 80 | module.exports.documentHasAchievedMajority = internal.documentHasAchievedMajority; 81 | 82 | /* 83 | * Check if a galaxy has voted on a proposal. 84 | * @param {Object} contracts - An Urbit contracts object. 85 | * @param {String} proposal - The target proposal address. 86 | * @return {Promise} True if so, false otherwise. 87 | */ 88 | module.exports.hasVotedOnUpgradePoll = internal.hasVotedOnUpgradePoll; 89 | 90 | /* 91 | * Check if a galaxy has voted on a proposal. 92 | * @param {Object} contracts - An Urbit contracts object. 93 | * @param {String} proposal - The target proposal address. 94 | * @return {Promise} True if so, false otherwise. 95 | */ 96 | module.exports.hasVotedOnDocumentPoll = internal.hasVotedOnDocumentPoll; 97 | 98 | /* 99 | * Check whether a proposal has achieved majority, updating the state and 100 | * sending an event if it has. 101 | * @param {Object} contracts - An Urbit contracts object. 102 | * @param {String} proposal - The target proposal address. 103 | * @return {Promise} True if so, false otherwise. 104 | */ 105 | module.exports.updateDocumentPoll = internal.updateDocumentPoll; 106 | 107 | -------------------------------------------------------------------------------- /internal/ecliptic.js: -------------------------------------------------------------------------------- 1 | 2 | const gen = require('./genTransaction') 3 | 4 | module.exports.owner = (contracts) => 5 | contracts.ecliptic.methods.owner().call() 6 | 7 | module.exports.balanceOf = (contracts, address) => 8 | contracts.ecliptic.methods.balanceOf(address).call() 9 | 10 | module.exports.ownerOf = (contracts, point) => 11 | contracts.ecliptic.methods.ownerOf(point).call() 12 | 13 | module.exports.exists = (contracts, point) => 14 | contracts.ecliptic.methods.exists(point).call() 15 | 16 | module.exports.getApproved = (contracts, point) => 17 | contracts.ecliptic.methods.getApproved(point).call() 18 | 19 | module.exports.isApprovedForAll = (contracts, owner, operator) => 20 | contracts.ecliptic.methods.isApprovedForAll(owner, operator).call() 21 | 22 | module.exports.getSpawnLimit = (contracts, point, time) => 23 | contracts.ecliptic.methods.getSpawnLimit(point, time).call() 24 | 25 | module.exports.canEscapeTo = (contracts, point, sponsor) => 26 | contracts.ecliptic.methods.canEscapeTo(point, sponsor).call() 27 | 28 | module.exports.safeTransferFrom = (contracts, _from, _to, _point) => 29 | gen.tx(contracts.ecliptic, 'safeTransferFrom', _from, _to, _point) 30 | 31 | module.exports.transferFrom = (contracts, _from, _to, _point) => 32 | gen.tx(contracts.ecliptic, 'transferFrom', _from, _to, _point) 33 | 34 | module.exports.approve = (contracts, _approved, _point) => 35 | gen.tx(contracts.ecliptic, 'approve', _approved, _point) 36 | 37 | module.exports.setApprovalForAll = (contracts, _operator, _approved) => 38 | gen.tx(contracts.ecliptic, 'setApprovalForAll', _operator, _approved) 39 | 40 | module.exports.setManagementProxy = (contracts, _point, _manager) => 41 | gen.tx(contracts.ecliptic, 'setManagementProxy', _point, _manager) 42 | 43 | module.exports.configureKeys = 44 | (contracts, _point, _encryptionKey, 45 | _authenticationKey, _cryptoSuiteVersion, _discontinuous) => 46 | gen.tx( 47 | contracts.ecliptic, 'configureKeys', _point, _encryptionKey, 48 | _authenticationKey, _cryptoSuiteVersion, _discontinuous) 49 | 50 | module.exports.spawn = (contracts, _point, _target) => 51 | gen.tx(contracts.ecliptic, 'spawn', _point, _target) 52 | 53 | module.exports.setSpawnProxy = (contracts, _prefix, _spawnProxy) => 54 | gen.tx(contracts.ecliptic, 'setSpawnProxy', _prefix, _spawnProxy) 55 | 56 | module.exports.transferPoint = (contracts, _point, _target, _reset) => 57 | gen.tx(contracts.ecliptic, 'transferPoint', _point, _target, _reset) 58 | 59 | module.exports.setTransferProxy = (contracts, _point, _transferProxy) => 60 | gen.tx(contracts.ecliptic, 'setTransferProxy', _point, _transferProxy) 61 | 62 | module.exports.escape = (contracts, _point, _sponsor) => 63 | gen.tx(contracts.ecliptic, 'escape', _point, _sponsor) 64 | 65 | module.exports.cancelEscape = (contracts, _point) => 66 | gen.tx(contracts.ecliptic, 'cancelEscape', _point) 67 | 68 | module.exports.adopt = (contracts, _escapee) => 69 | gen.tx(contracts.ecliptic, 'adopt', _escapee) 70 | 71 | module.exports.reject = (contracts, _escapee) => 72 | gen.tx(contracts.ecliptic, 'reject', _escapee) 73 | 74 | module.exports.detach = (contracts, _point) => 75 | gen.tx(contracts.ecliptic, 'detach', _point) 76 | 77 | module.exports.setVotingProxy = (contracts, _galaxy, _proxy) => 78 | gen.tx(contracts.ecliptic, 'setVotingProxy', _galaxy, _proxy) 79 | 80 | module.exports.startUpgradePoll = (contracts, _galaxy, _proposal) => 81 | gen.tx(contracts.ecliptic, 'startUpgradePoll', _galaxy, _proposal) 82 | 83 | module.exports.startDocumentPoll = (contracts, _galaxy, _proposal) => 84 | gen.tx(contracts.ecliptic, 'startDocumentPoll', _galaxy, _proposal) 85 | 86 | module.exports.castUpgradeVote = (contracts, _galaxy, _proposal, _vote) => 87 | gen.tx(contracts.ecliptic, 'castUpgradeVote', _galaxy, _proposal, _vote) 88 | 89 | module.exports.castDocumentVote = (contracts, _galaxy, _proposal, _vote) => 90 | gen.tx(contracts.ecliptic, 'castDocumentVote', _galaxy, _proposal, _vote) 91 | 92 | module.exports.updateUpgradePoll = (contracts, _proposal) => 93 | gen.tx(contracts.ecliptic, 'updateUpgradePoll', _proposal) 94 | 95 | module.exports.updateDocumentPoll = (contracts, _proposal) => 96 | gen.tx(contracts.ecliptic, 'updateDocumentPoll', _proposal) 97 | 98 | module.exports.createGalaxy = (contracts, _galaxy, _target) => 99 | gen.tx(contracts.ecliptic, 'createGalaxy', _galaxy, _target) 100 | 101 | module.exports.setDnsDomains = (contracts, _primary, _secondary, _tertiary) => 102 | gen.tx(contracts.ecliptic, 'setDnsDomains', _primary, _secondary, _tertiary) 103 | 104 | -------------------------------------------------------------------------------- /contracts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contracts API 3 | * @module contracts 4 | */ 5 | 6 | const eclipticAbi = 7 | require('azimuth-solidity/build/contracts/Ecliptic.json').abi; 8 | 9 | const azimuthAbi = 10 | require('azimuth-solidity/build/contracts/Azimuth.json').abi; 11 | 12 | const pollsAbi = 13 | require('azimuth-solidity/build/contracts/Polls.json').abi; 14 | 15 | const claimsAbi = 16 | require('azimuth-solidity/build/contracts/Claims.json').abi; 17 | 18 | const linearStarReleaseAbi = 19 | require('azimuth-solidity/build/contracts/LinearStarRelease.json').abi; 20 | 21 | const delegatedSendingAbi = 22 | require('azimuth-solidity/build/contracts/DelegatedSending.json').abi; 23 | 24 | const conditionalStarReleaseAbi = 25 | require('azimuth-solidity/build/contracts/ConditionalStarRelease.json').abi; 26 | 27 | /** 28 | * Create a collection of Urbit contracts, given a web3 instance and their 29 | * provided addresses. 30 | * @param {Object} web3 - A web3 instance. 31 | * @param {Object} addresses - An addresses object. Must provide addresses for 32 | * ecliptic, azimuth, and polls contracts, at those respective key names. 33 | * @return {Object} The initialised contracts. 34 | */ 35 | const initContracts = (web3, addresses) => { 36 | let contracts = {}; 37 | contracts = newEcliptic(contracts, web3, addresses.ecliptic); 38 | contracts = newAzimuth(contracts, web3, addresses.azimuth); 39 | contracts = newPolls(contracts, web3, addresses.polls); 40 | contracts = newClaims(contracts, web3, addresses.claims); 41 | contracts = newLinearStarRelease(contracts, web3, addresses.linearSR); 42 | contracts = newConditionalStarRelease(contracts, web3, addresses.conditionalSR); 43 | contracts = newDelegatedSending(contracts, web3, addresses.delegatedSending); 44 | return contracts; 45 | }; 46 | 47 | /** 48 | * Initialise as many Urbit contracts as possible, given a azimuth contract 49 | * address. 50 | * @param {Object} web3 - A web3 instance. 51 | * @param {String} azimuthAddress - An address to a azimuth contract. 52 | * @return {Object} The initialised contracts. 53 | */ 54 | const initContractsPartial = async (web3, azimuthAddress) => { 55 | let contracts = {}; 56 | contracts = newAzimuth(contracts, web3, azimuthAddress); 57 | let eclipticAddress = await contracts.azimuth.methods.owner().call(); 58 | contracts = newEcliptic(contracts, web3, eclipticAddress); 59 | let pollsAddress = await contracts.ecliptic.methods.polls().call(); 60 | contracts = newPolls(contracts, web3, pollsAddress); 61 | let claimsAddress = await contracts.ecliptic.methods.claims().call(); 62 | contracts = newClaims(contracts, web3, claimsAddress); 63 | return contracts; 64 | } 65 | 66 | const newContract = (web3, address, abi) => { 67 | let contract = new web3.eth.Contract(abi, address); 68 | //NOTE this allows us to support a broader range of web3 versions. 69 | // see also #23. 70 | contract._address = (contract._address || contract.address); 71 | contract.address = contract._address; 72 | return contract; 73 | } 74 | 75 | const newEcliptic = (contracts, web3, address) => { 76 | contracts = contracts || {}; 77 | contracts.ecliptic = newContract(web3, address, eclipticAbi); 78 | return contracts; 79 | } 80 | 81 | const newAzimuth = (contracts, web3, address) => { 82 | contracts = contracts || {}; 83 | contracts.azimuth = newContract(web3, address, azimuthAbi); 84 | return contracts; 85 | } 86 | 87 | const newPolls = (contracts, web3, address) => { 88 | contracts = contracts || {}; 89 | contracts.polls = newContract(web3, address, pollsAbi); 90 | return contracts; 91 | } 92 | 93 | const newClaims = (contracts, web3, address) => { 94 | contracts = contracts || {}; 95 | contracts.claims = newContract(web3, address, claimsAbi); 96 | return contracts; 97 | } 98 | 99 | const newLinearStarRelease = (contracts, web3, address) => { 100 | contracts = contracts || {}; 101 | contracts.linearSR = newContract(web3, address, linearStarReleaseAbi); 102 | return contracts; 103 | } 104 | 105 | const newConditionalStarRelease = (contracts, web3, address) => { 106 | contracts = contracts || {}; 107 | contracts.conditionalSR = newContract(web3, address, conditionalStarReleaseAbi); 108 | return contracts; 109 | } 110 | 111 | const newDelegatedSending = (contracts, web3, address) => { 112 | contracts = contracts || {}; 113 | contracts.delegatedSending = newContract(web3, address, delegatedSendingAbi); 114 | return contracts; 115 | } 116 | 117 | module.exports = { 118 | initContracts, 119 | initContractsPartial, 120 | // 121 | newEcliptic, 122 | newAzimuth, 123 | newPolls, 124 | newClaims, 125 | newLinearStarRelease, 126 | newDelegatedSending, 127 | newConditionalStarRelease 128 | } 129 | -------------------------------------------------------------------------------- /internal/azimuth.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports.owner = (contracts) => 3 | contracts.azimuth.methods.owner().call() 4 | 5 | module.exports.getPoint = (contracts, point) => 6 | contracts.azimuth.methods.points(point).call() 7 | 8 | module.exports.getRights = (contracts, point) => 9 | contracts.azimuth.methods.rights(point).call() 10 | 11 | module.exports.getOwnedPoints = (contracts, address) => 12 | contracts.azimuth.methods.getOwnedPoints(address).call() 13 | 14 | module.exports.getOwnedPointCount = (contracts, address) => 15 | contracts.azimuth.methods.getOwnedPointCount(address).call() 16 | 17 | module.exports.getOwnedPointAtIndex = (contracts, address, idx) => 18 | contracts.azimuth.methods.getOwnedPointAtIndex(address, idx).call() 19 | 20 | module.exports.isOwner = (contracts, point, address) => 21 | contracts.azimuth.methods.isOwner(point, address).call() 22 | 23 | module.exports.getOwner = (contracts, point) => 24 | contracts.azimuth.methods.getOwner(point).call() 25 | 26 | module.exports.isManagementProxy = (contracts, owner, manager) => 27 | contracts.azimuth.methods.isManagementProxy(owner, manager).call() 28 | 29 | module.exports.getManagementProxy = (contracts, point) => 30 | contracts.azimuth.methods.getManagementProxy(point).call() 31 | 32 | module.exports.canManage = (contracts, point, address) => 33 | contracts.azimuth.methods.canManage(point, address).call() 34 | 35 | module.exports.getManagerForCount = (contracts, address) => 36 | contracts.azimuth.methods.getManagerForCount(address).call() 37 | 38 | module.exports.getManagerFor = (contracts, address) => 39 | contracts.azimuth.methods.getManagerFor(address).call() 40 | 41 | module.exports.isVotingProxy = (contracts, owner, delegate) => 42 | contracts.azimuth.methods.isVotingProxy(owner, delegate).call() 43 | 44 | module.exports.canVoteAs = (contracts, point, address) => 45 | contracts.azimuth.methods.canVoteAs(point, address).call() 46 | 47 | module.exports.getVotingForCount = (contracts, address) => 48 | contracts.azimuth.methods.getVotingForCount(address).call() 49 | 50 | module.exports.getVotingFor = (contracts, address) => 51 | contracts.azimuth.methods.getVotingFor(address).call() 52 | 53 | module.exports.isActive = (contracts, point) => 54 | contracts.azimuth.methods.isActive(point).call() 55 | 56 | module.exports.getKeys = (contracts, point) => 57 | contracts.azimuth.methods.getKeys(point).call() 58 | 59 | module.exports.getKeyRevisionNumber = (contracts, point) => 60 | contracts.azimuth.methods.getKeyRevisionNumber(point).call() 61 | 62 | module.exports.hasBeenLinked = (contracts, point) => 63 | contracts.azimuth.methods.hasBeenLinked(point).call() 64 | 65 | module.exports.isLive = (contracts, point) => 66 | contracts.azimuth.methods.isLive(point).call() 67 | 68 | module.exports.getContinuityNumber = (contracts, point) => 69 | contracts.azimuth.methods.getContinuityNumber(point).call() 70 | 71 | module.exports.getSpawnCount = (contracts, point) => 72 | contracts.azimuth.methods.getSpawnCount(point).call() 73 | 74 | module.exports.getSpawned = (contracts, point) => 75 | contracts.azimuth.methods.getSpawned(point).call() 76 | 77 | module.exports.getSponsor = (contracts, point) => 78 | contracts.azimuth.methods.getSponsor(point).call() 79 | 80 | module.exports.getSponsoring = (contracts, point) => 81 | contracts.azimuth.methods.getSponsoring(point).call() 82 | 83 | module.exports.getSponsoringCount = (contracts, point) => 84 | contracts.azimuth.methods.getSponsoringCount(point).call() 85 | 86 | module.exports.hasSponsor = (contracts, point) => 87 | contracts.azimuth.methods.hasSponsor(point).call() 88 | 89 | module.exports.isSponsor = (contracts, point, sponsor) => 90 | contracts.azimuth.methods.isSponsor(point, sponsor).call() 91 | 92 | module.exports.isEscaping = (contracts, point) => 93 | contracts.azimuth.methods.isEscaping(point).call() 94 | 95 | module.exports.getEscapeRequest = (contracts, point) => 96 | contracts.azimuth.methods.getEscapeRequest(point).call() 97 | 98 | module.exports.getEscapeRequests = (contracts, point) => 99 | contracts.azimuth.methods.getEscapeRequests(point).call() 100 | 101 | module.exports.getEscapeRequestsCount = (contracts, point) => 102 | contracts.azimuth.methods.getEscapeRequestsCount(point).call() 103 | 104 | module.exports.isRequestingEscapeTo = (contracts, point, sponsor) => 105 | contracts.azimuth.methods.isRequestingEscapeTo(point, sponsor).call() 106 | 107 | module.exports.isSpawnProxy = (contracts, point, address) => 108 | contracts.azimuth.methods.isSpawnProxy(point, address).call() 109 | 110 | module.exports.getSpawnProxy = (contracts, point) => 111 | contracts.azimuth.methods.getSpawnProxy(point).call() 112 | 113 | module.exports.getSpawningForCount = (contracts, address) => 114 | contracts.azimuth.methods.getSpawningForCount(address).call() 115 | 116 | module.exports.getSpawningFor = (contracts, address) => 117 | contracts.azimuth.methods.getSpawningFor(address).call() 118 | 119 | module.exports.isTransferProxy = (contracts, point, address) => 120 | contracts.azimuth.methods.isTransferProxy(point, address).call() 121 | 122 | module.exports.getTransferProxy = (contracts, point) => 123 | contracts.azimuth.methods.getTransferProxy(point).call() 124 | 125 | module.exports.getTransferringForCount = (contracts, address) => 126 | contracts.azimuth.methods.getTransferringForCount(address).call() 127 | 128 | module.exports.getTransferringFor = (contracts, address) => 129 | contracts.azimuth.methods.getTransferringFor(address).call() 130 | 131 | module.exports.isOperator = (contracts, owner, operator) => 132 | contracts.azimuth.methods.isOperator(owner, operator).call() 133 | -------------------------------------------------------------------------------- /conditionalSR.js: -------------------------------------------------------------------------------- 1 | /*** 2 | * conditionalSR API 3 | * @module conditionalSR 4 | */ 5 | 6 | const internal = require('./internal/conditionalSR'); 7 | 8 | /** 9 | * Return the details of a commitment. 10 | * @param {Object} contracts - An Urbit contracts object. 11 | * @param {String} address - The participant/registered address for the 12 | * commitment. 13 | * @return {Promise} A commitment object, with windup, rate, rateUnit, 14 | * amount, withdrawn. 15 | */ 16 | module.exports.getCommitment = internal.getCommitment; 17 | 18 | /** 19 | * Return the list of stars that have been deposited into, but not yet 20 | * withdrawn from a commitment. 21 | * @param {Object} contracts - An Urbit contracts object. 22 | * @param {String} address - The participant/registered address for the 23 | * commitment. 24 | * @return {Promise>} The stars left in the commitment. 25 | */ 26 | module.exports.getRemainingStars = internal.getRemainingStars; 27 | 28 | /** 29 | * Return the configured sizes of the batches for the commitment. 30 | * @param {Object} contracts - An Urbit contracts object. 31 | * @param {String} address - The participant/registered address for the 32 | * commitment. 33 | * @return {Promise>} The batch sizes for the commitment. 34 | */ 35 | module.exports.getBatches = internal.getBatches; 36 | 37 | /** 38 | * Return whether the amount of stars deposited into the commitment checks out. 39 | * @param {Object} contracts - An Urbit contracts object. 40 | * @param {String} address - The participant/registered address for the 41 | * commitment. 42 | * @return {Promise} true if sufficient stars have been deposited. 43 | */ 44 | module.exports.verifyBalance = internal.verifyBalance; 45 | 46 | /** 47 | * Return the timestamp at which the release was started. 48 | * @param {Object} contracts - An Urbit contracts object. 49 | * @return {Promise} A timestamp. 50 | */ 51 | module.exports.getStartTime = internal.getStartTime; 52 | 53 | /** 54 | * Return the amount of stars a participant has already withdrawn from 55 | * each of their batches at the current time 56 | * @param {Object} contracts - An Urbit contracts object. 57 | * @param {String} address - The participant/registered address for the 58 | * commitment. 59 | * @return {Promise>} the number of stars already withdrawn for 60 | * each batch. 61 | */ 62 | module.exports.getWithdrawn = internal.getWithdrawn; 63 | /** 64 | * Return the amount of stars a participant is allowed to withdraw from 65 | * one of their batches at the current time. 66 | * @param {Object} contracts - An Urbit contracts object. 67 | * @param {String} address - The participant/registered address for the 68 | * commitment. 69 | * @param {Number} batch - The batch number to look up 70 | * @return {Promise} the withdraw limit. 71 | */ 72 | module.exports.getWithdrawLimit = internal.getWithdrawLimit; 73 | 74 | /** 75 | * Return the address this commitment can be transferred to. 76 | * @param {Object} contracts - An Urbit contracts object. 77 | * @param {String} address - The participant/registered address for the 78 | * commitment. 79 | * @return {Promise} The approved transfer address, 0x0 for none. 80 | */ 81 | module.exports.getApprovedTransfer = internal.getApprovedTransfer; 82 | 83 | /** 84 | * Return conditions configuration and state data. 85 | * @param {Object} contracts - An Urbit contracts object. 86 | * @return {Promise} An object containing conditions state, with 87 | * { conditions, livelines, deadlines, timestamps } arrays. 88 | */ 89 | module.exports.getConditionsState = async function(contracts) { 90 | let { conds, deads, lives, times } = await internal.getConditionsState( 91 | contracts 92 | ); 93 | return { 94 | conditions: conds, 95 | livelines: lives, 96 | deadlines: deads, 97 | timestamps: times 98 | }; 99 | }; 100 | 101 | /** 102 | * Return whether or not each of the batches have been forfeited at the 103 | * current time. 104 | * @param {Object} contracts - An Urbit contracts object. 105 | * @param {String} address - The participant/registered address for the 106 | * commitment. 107 | * @return {Promise>} the number of stars already withdrawn for 108 | * each batch. 109 | */ 110 | module.exports.getForfeited = internal.getForfeited; 111 | 112 | /** 113 | * Approve the transfer of a commitment to another address. 114 | * @param {Object} contracts - An Urbit contracts object. 115 | * @param {String} address - The address to transfer to. 116 | * @return {Promise} An unsigned transaction object. 117 | */ 118 | module.exports.approveCommitmentTransfer = internal.approveCommitmentTransfer; 119 | 120 | /** 121 | * Make an approved transfer of the specified commitment to the caller's address. 122 | * @param {Object} contracts - An Urbit contracts object. 123 | * @param {String} address - The address to transfer from. 124 | * @return {Object} An unsigned transaction object. 125 | */ 126 | module.exports.transferCommitment = internal.transferCommitment; 127 | 128 | /** 129 | * Withdraw one star from a batch to the caller's address. 130 | * @param {Object} contracts - An Urbit contracts object. 131 | * @param {Number} batch - The batch number to withdraw from 132 | * @return {Object} An unsigned transaction object. 133 | */ 134 | module.exports.withdraw = internal.withdraw; 135 | 136 | /** 137 | * Withdraw one star from a batch to the specified address. 138 | * @param {Object} contracts - An Urbit contracts object. 139 | * @param {Number} batch - The batch number 140 | * @param {String} address - The address to withdraw to. 141 | * @return {Object} An unsigned transaction object. 142 | */ 143 | module.exports.withdrawTo = internal.withdrawTo; 144 | 145 | /** 146 | * Forfeit stars contained in a batch with missed deadline, and all after it. 147 | * @param {Object} contracts - An Urbit contracts object. 148 | * @param {Number} batch - The condition/batch to base forfeiture off. 149 | * @return {Object} An unsigned transaction object. 150 | */ 151 | module.exports.forfeit = internal.forfeit; 152 | 153 | /** 154 | * Analyze a condition for satisfaction. 155 | * @param {Object} contracts - An Urbit contracts object. 156 | * @param {String} condition - The condition (index) to analyze. 157 | * @return {Object} An unsigned transaction object. 158 | */ 159 | module.exports.analyzeCondition = internal.analyzeCondition; 160 | -------------------------------------------------------------------------------- /delegatedSending.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Delegated Sending 3 | * @module delegatedSending 4 | */ 5 | 6 | const internal = require('./internal/delegatedSending'); 7 | const azimuth = require('./azimuth'); 8 | const ecliptic = require('./ecliptic'); 9 | 10 | /** 11 | * Return the amount of invites left in the pool 12 | * @param {Number} pool - Pool number 13 | * @param {Number} prefix - Invites from this prefix 14 | * @return {Promise} Number of invites remaining 15 | */ 16 | module.exports.pools = internal.pools 17 | module.exports.invitesInPool = internal.pools 18 | 19 | /** 20 | * Return the points invited by point 21 | * @param {Number} point - Point number 22 | * @return {Promise>} Points invited by point 23 | */ 24 | module.exports.getInvited = internal.getInvited 25 | 26 | /** 27 | * Return the point that point was invited by 28 | * @param {Number} point - Point number 29 | * @return {Promise} The inviter point (0 if not invited) 30 | */ 31 | module.exports.invitedBy = internal.invitedBy 32 | 33 | /** 34 | * Returns true if as can send point, false otherwise 35 | * @param {Number} as - The inviter 36 | * @param {Number} point - The point to send 37 | * @return {Promise} Whether as can send point 38 | */ 39 | module.exports.canSend = internal.canSend 40 | 41 | /** 42 | * Get the invite pool point belongs to 43 | * @param {Number} point - Point number 44 | * @return {Promise} Pool number 45 | */ 46 | module.exports.getPool = internal.getPool 47 | module.exports.invitingFromPool = internal.getPool 48 | 49 | /** 50 | * Get the stars that have put invites into the pool 51 | * @param {Number} pool - Pool number 52 | * @return {Promise>} Stars that touched the pool 53 | */ 54 | module.exports.getPoolStars = internal.getPoolStars 55 | 56 | /** 57 | * Returns true if receipients is eligible to receive a point, false otherwise 58 | * @param {String} recipient - Ethereum address 59 | * @return {Promise} Whether recipient can receive a point 60 | */ 61 | module.exports.canReceive = internal.canReceive 62 | 63 | /** 64 | * Returns the total amount of usable invites available to point. 65 | * Invites are usable if the star they're associated with has its spawn proxy 66 | * set to the Delegated Sending contract, and is still under its spawn limit. 67 | * @param {Number} point - The point whose invites to count 68 | * @return {Promise} Total amount of invites 69 | */ 70 | module.exports.getTotalUsableInvites = async function(contracts, point) { 71 | const pool = await internal.getPool(contracts, point); 72 | const stars = await internal.getPoolStars(contracts, pool); 73 | let counts = stars.map(async star => { 74 | const capable = azimuth.isSpawnProxy( 75 | contracts, star, contracts.delegatedSending.address 76 | ); 77 | const supportive = azimuth.isLive(contracts, star); 78 | if (!(await capable) || !(await supportive)) return 0; 79 | const invites = await internal.pools(contracts, pool, star); 80 | const spawnable = await ecliptic.getSpawnsRemaining(contracts, star); 81 | return Math.min(invites, spawnable); 82 | }); 83 | counts = await Promise.all(counts); 84 | return counts.reduce((total, count) => (total + count), 0); 85 | } 86 | 87 | /** 88 | * Generate a list of planets for as to send as invites 89 | * NOTE that the returned list isn't guaranteed to contain exactly amount items, 90 | * it may return fewer in cases where not enough invites are available, 91 | * usable, or spawn limits are being hit 92 | * @param {Number} as - point to send the planets with 93 | * @param {Number} amount - amount of planets to generate 94 | * @return {Promise>} Pseudo-random list of planets that as can send 95 | */ 96 | module.exports.getPlanetsToSend = async function(contracts, as, amount) { 97 | const sponsor = await azimuth.getSponsor(contracts, as); 98 | const inviter = await internal.invitedBy(contracts, as); 99 | const inviterSponsor = await azimuth.getSponsor(contracts, inviter); 100 | const pool = await internal.getPool(contracts, as); 101 | let stars = await internal.getPoolStars(contracts, pool); 102 | 103 | // assign priorities so that we can order them: 104 | // sponsor > inviter's sponsor > least spawned > most spawned 105 | stars = stars.map(async star => { 106 | const available = await internal.pools(contracts, pool, star); 107 | const capable = azimuth.isSpawnProxy( 108 | contracts, star, contracts.delegatedSending.address 109 | ); 110 | const supportive = azimuth.isLive(contracts, star); 111 | if (available === 0 || !(await capable) || !(await supportive)) 112 | return {star, available: 0}; 113 | 114 | const spawned = await azimuth.getSpawnCount(contracts, star); 115 | let priority; 116 | if (star === sponsor) 117 | priority = -2; 118 | else if (star === inviterSponsor) 119 | priority = -1; 120 | else if (priority === 0) 121 | priority = spawned; 122 | 123 | return {star: Number(star), available, priority}; 124 | }); 125 | stars = await Promise.all(stars); 126 | stars = stars.filter(a => (a.available > 0)); 127 | stars = stars.sort((a, b) => (a.priority - b.priority)); 128 | 129 | let s = 0; 130 | let planets = []; 131 | while (amount > 0 && s < stars.length) { 132 | let star = stars[s]; 133 | const spawnable = await ecliptic.getSpawnsRemaining(contracts, star.star); 134 | const get = Math.min(star.available, spawnable, amount); 135 | const unspawned = await azimuth.getUnspawnedChildren(contracts, star.star); 136 | 137 | // make sure the first couple elements are randomized 138 | for (let i = 0; i < get; i++) { 139 | const j = Math.floor(Math.random() * unspawned.length); 140 | [unspawned[i], unspawned[j]] = [unspawned[j], unspawned[i]]; 141 | } 142 | 143 | // push number of planets to output list 144 | for (let i = 0; i < get; i++) { 145 | planets.push(unspawned[i]); 146 | } 147 | amount = amount - get; 148 | s++; 149 | } 150 | return planets; 151 | } 152 | 153 | 154 | /** 155 | * Give for (and their invite tree) access to size invites 156 | * @param {Number} as - prefix to give invites as 157 | * @param {Number} for - point to give invites to 158 | * @param {Number} size - amount of invites to give 159 | * @return {Object} An unsigned transaction object 160 | */ 161 | module.exports.setPoolSize = internal.setPoolSize 162 | 163 | /** 164 | * As as, send the point to to 165 | * @param {Number} as - point to send the invite as 166 | * @param {Number} point - the point to send as an invite 167 | * @param {String} to - target Ethereum address 168 | * @return {Object} An unsigned transaction object 169 | */ 170 | module.exports.sendPoint = internal.sendPoint 171 | 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # azimuth-js 2 | 3 | [![Build Status](https://secure.travis-ci.org/urbit/azimuth-js.png)](http://travis-ci.org/urbit/azimuth-js) 4 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/urbit/azimuth-js/blob/master/LICENSE) 5 | [![npm](https://img.shields.io/npm/v/azimuth-js.svg)](https://www.npmjs.com/package/azimuth-js) 6 | 7 | Interact with [Azimuth](https://github.com/urbit/azimuth) from 8 | Javascript. 9 | 10 | ## Install 11 | 12 | Just grab from npm like so: 13 | 14 | ``` 15 | npm install azimuth-js 16 | ``` 17 | 18 | ## API Reference 19 | 20 | [./docs/](./docs/api.md) 21 | 22 | ## Quickstart 23 | 24 | (This example uses an [Infura](https://infura.io/) endpoint as a provider for 25 | web3.) 26 | 27 | ``` 28 | const ajs = require('azimuth-js') 29 | const Web3 = require('web3') 30 | 31 | const infura = `https://mainnet.infura.io/v3/${MY_INFURA_ID}` 32 | const provider = new Web3.providers.HttpProvider(infura) 33 | const web3 = new Web3(provider) 34 | 35 | const contracts = await ajs.initContractsPartial(web3, ajs.azimuth.mainnet) 36 | 37 | const zod = '0x9F57C77b1095BD5Db0558b9Cb9b8e6Fc67375E3C' 38 | 39 | ajs.azimuth.isOwner(contracts, 0, zod).then(console.log) // true 40 | ``` 41 | 42 | ## Usage 43 | 44 | Require the library via something like: 45 | 46 | ```javascript 47 | const azimuthjs = require('azimuth-js'); 48 | ``` 49 | 50 | In general: use the functions in `azimuthjs.ecliptic`, `azimuthjs.azimuth`, 51 | `azimuthjs.polls`, and so on to interact with the corresponding Ethereum 52 | contract. Use `azimuthjs.check` to verify any required state is what you expect 53 | it to be. `azimuthjs.txn` contains functions for signing and sending 54 | transactions, and `azimuthjs.utils` mostly re-exports useful utility functions 55 | from [ethereumjs-util](https://github.com/ethereumjs/ethereumjs-util). 56 | 57 | You might want to define something like the following, for convenience: 58 | 59 | ```javascript 60 | const ecliptic = azimuthjs.ecliptic; 61 | const azimuth = azimuthjs.azimuth; 62 | const check = azimuthjs.check; 63 | const txn = azimuthjs.txn 64 | ``` 65 | 66 | The library exposes a purely-functional API. This means you'll have to supply 67 | your own state (e.g. web3 instance, contracts instance) whenever dealing with 68 | transactions and contract initialisation. For example, when running a fresh 69 | local Ganache node with the appropriate mnemonic (see below), this will get you 70 | set up: 71 | 72 | ```javascript 73 | const Web3 = require('web3'); 74 | 75 | let provider = new Web3.providers.HttpProvider('http://localhost:8545'); 76 | let web3 = new Web3(provider); 77 | 78 | let contractAddresses = { 79 | ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480', 80 | azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381', 81 | polls: '0x935452c45eda2958976a429c9733c40302995efd', 82 | claims: '0xe0834579269eac6beca2882a6a21f6fb0b1d7196' 83 | } 84 | 85 | let contracts = azimuthjs.initContracts(web3, contractAddresses); 86 | ``` 87 | 88 | Note that the web3 object is passed to `azimuthjs.initContracts` explicitly. 89 | Aside from contract initialisation, this is typically only required when 90 | sending transactions (more below). 91 | 92 | When interacting with the contract APIs, on the other hand, you'll almost 93 | always have to pass a contracts object explicitly. For example: 94 | 95 | ```javascript 96 | // ecliptic owner 97 | const owner = '0x6deffb0cafdb11d175f123f6891aa64f01c24f7d'; 98 | 99 | const galaxy = 42; 100 | 101 | check.canCreateGalaxy(contracts, galaxy, owner); 102 | ``` 103 | 104 | Note that the 'contracts' object initialised previously is passed as the first 105 | argument. Again, this is almost always the case. 106 | 107 | Most of the exposed contracts API consists of functions that, at most, read 108 | from the Ethereum chain state, returning some result in a Promise. The primary 109 | exceptions are some of the functions in the 'ecliptic' contract; for those 110 | that modify chain state, the function will return a transaction object, e.g.: 111 | 112 | ```javascript 113 | let tx = ecliptic.createGalaxy(contracts, galaxy, owner); 114 | ``` 115 | 116 | To modify contract state, you'll have to sign ('signTransaction') and send 117 | ('sendSignedTransaction') the transaction explicitly. For example: 118 | 119 | ```javascript 120 | txn.signTransaction(web3, tx, pk).then(stx => 121 | txn.sendSignedTransaction(web3, stx)); 122 | ``` 123 | 124 | or, in the body of an `async` function, you can use `await`: 125 | 126 | ```javascript 127 | let stx = await txn.signTransaction(web3, tx, pk); 128 | txn.sendSignedTransaction(web3, stx); 129 | ``` 130 | 131 | Note again that, when dealing with transactions, a web3 object must be passed 132 | as the first argument. 133 | 134 | Many of the functions for the 'azimuth' contract will work when the function is 135 | passed either a point identifier (i.e. an unsigned integer), meaning the 136 | computation will be carried out on-chain, or a point object (i.e. something that 137 | has been retrieved via 'azimuth.getPoint'), meaning the computation will be 138 | carried out purely, simply by reference to the point object. The result is 139 | wrapped in a Promise, in either case. 140 | 141 | Functions that use Web3 may throw. The thrown object will always contain at 142 | least 'name' and 'message' properties. Tread carefully when using Web3 while 143 | offline. 144 | 145 | Contract action checks ('canXYZ') return result objects in the form of `{ 146 | result: bool, reason: string }`, where 'reason' is only set when 'result' is 147 | 'false'. These can't resolve when offline. 148 | 149 | ## Development 150 | 151 | ### Library Structure 152 | 153 | The modules found in the `internal` directory are intended to be fairly close 154 | mappings to the public, external, or view functions located in the contracts 155 | themselves. Mostly these are re-exported via the user-facing API, defined in 156 | `ecliptic.js` and friends. 157 | 158 | The one notable exception is in the `azimuth` module, where the behaviour of a 159 | function can often depend on the type of the argument passed to it. If one 160 | passes them a cached `point` object (retrieved via `getPoint`), then these 161 | functions will compute their values locally; if one supplies them with a point 162 | number (i.e., an integer), they will instead hit the network. 163 | 164 | ## Testing 165 | 166 | Use a simple: 167 | 168 | `npm test` 169 | 170 | to run the tests on a one-off local Ganache node. 171 | 172 | ### Local Testnet 173 | 174 | For debugging and custom testing, you'll need a local testnet running Azimuth. 175 | 176 | 1. Clone [Azimuth](https://github.com/urbit/azimuth) 177 | 2. `cd` into the repo and `npm install` 178 | 3. `npm install -g ganache-cli` 179 | 3. Run a local `ganache` node, boot using the following command to ensure a matching seed: 180 | `ganache-cli -m "benefit crew supreme gesture quantum web media hazard theory mercy wing kitten"` 181 | 4. Run `truffle deploy` from the Azimuth directory to deploy to your local node. 182 | -------------------------------------------------------------------------------- /ecliptic.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Ecliptic API 3 | * @module ecliptic 4 | */ 5 | 6 | const internal = require('./internal/ecliptic'); 7 | const azimuth = require('./azimuth'); 8 | 9 | /** 10 | * Get ecliptic contract owner. 11 | * @param {Object} contracts - An Urbit contracts object. 12 | * @return {Promise} The owner address. 13 | */ 14 | module.exports.owner = internal.owner; 15 | 16 | /** 17 | * Get the amount of points owned by an address. 18 | * @param {Object} contracts - An Urbit contracts object. 19 | * @param {String} address - Owner's address. 20 | * @return {Promise} Number of azimuth. 21 | */ 22 | module.exports.balanceOf = internal.balanceOf; 23 | 24 | /** 25 | * Get the current owner of a point. 26 | * @param {Object} contracts - An Urbit contracts object. 27 | * @param {Number} pointId - Point number. 28 | * @return {Promise} Owner's address. 29 | */ 30 | module.exports.ownerOf = internal.ownerOf; 31 | 32 | /** 33 | * Check if a point is active. 34 | * @param {Object} contracts - An Urbit contracts object. 35 | * @param {Number} pointId - Point number. 36 | * @return {Promise} true if point is active, false otherwise. 37 | */ 38 | module.exports.exists = internal.exists; 39 | 40 | /** 41 | * Get the transfer proxy for a point. 42 | * @param {Object} contracts - An Urbit contracts object. 43 | * @param {Number} pointId - Point number. 44 | * @return {Promise} The transfer proxy's address. 45 | */ 46 | module.exports.getApproved = internal.getApproved; 47 | 48 | /** 49 | * Check if an address is an operator for an owner. 50 | * @param {Object} contracts - An Urbit contracts object. 51 | * @param {String} owner - The owner's address. 52 | * @param {String} operator - The operator's address. 53 | * @return {Promise} true if 'operator' is an operator for 'owner'. 54 | */ 55 | module.exports.isApprovedForAll = internal.isApprovedForAll; 56 | 57 | /** 58 | * Return the total number of children a point is allowed to spawn at some time. 59 | * @param {Object} contracts - An Urbit contracts object. 60 | * @param {Number} point - Point number. 61 | * @param {Number} time - Time (uint256). 62 | * @return {Promise} The spawn limit. 63 | */ 64 | module.exports.getSpawnLimit = internal.getSpawnLimit; 65 | 66 | /** 67 | * Check if a point can escape to a sponsor. 68 | * @param {Object} contracts - An Urbit contracts object. 69 | * @param {Number} point - Point number. 70 | * @param {Number} sponsor - Sponsor's point number. 71 | * @return {Promise} True if point can escape, false otherwise. 72 | */ 73 | module.exports.canEscapeTo = internal.canEscapeTo; 74 | 75 | /** 76 | * Get the amount of children point can still spawn before hitting the limit. 77 | * @param {Object} contracts - An Urbit contracts object. 78 | * @param {Number} point - Point number. 79 | * @return {Promise} The amount of children still spawnable from point. 80 | */ 81 | module.exports.getSpawnsRemaining = async function(contracts, point) { 82 | const now = Math.floor(new Date().getTime() / 1000); 83 | const count = await azimuth.getSpawnCount(contracts, point); 84 | const limit = await internal.getSpawnLimit(contracts, point, now); 85 | return limit - count; 86 | } 87 | 88 | 89 | /** 90 | * Safely transfer a point between addresses (call recipient if it's a contract). 91 | * @param {Object} contracts - An Urbit contracts object. 92 | * @param {String} from - Sender's address. 93 | * @param {String} to - Receiver's address. 94 | * @param {Number} pointId - Point number. 95 | * @return {Object} An unsigned transaction object. 96 | */ 97 | module.exports.safeTransferFrom = internal.safeTransferFrom; 98 | 99 | /** 100 | * Transfer a point between addresses (without notifying recipient contract). 101 | * @param {Object} contracts - An Urbit contracts object. 102 | * @param {String} from - Sender's address. 103 | * @param {String} to - Receiver's address. 104 | * @param {Number} pointId - Point number. 105 | * @return {Object} An unsigned transaction object. 106 | */ 107 | module.exports.transferFrom = internal.transferFrom; 108 | 109 | /** 110 | * Allow an address to transfer ownership of a point. 111 | * @param {Object} contracts - An Urbit contracts object. 112 | * @param {String} approved - The approved address. 113 | * @param {Number} pointId - Point number. 114 | * @return {Object} An unsigned transaction object. 115 | */ 116 | module.exports.approve = internal.approve; 117 | 118 | /** 119 | * Allow or disallow an operator to transfer ownership of all points owned by 120 | * the message sender. 121 | * @param {Object} contracts - An Urbit contracts object. 122 | * @param {Address} operator - The operator's address. 123 | * @param {Bool} approved - Whether the operator is approved or not. 124 | * @return {Object} An unsigned transaction object. 125 | */ 126 | module.exports.setApprovalForAll = internal.setApprovalForAll; 127 | 128 | /** 129 | * Configure the management address for a point owned by the message sender. 130 | * @param {Object} contracts - An Urbit contracts object. 131 | * @param {Number} point - The point to manage. 132 | * @param {String} manager - The management address. 133 | * @return {Object} An unsigned transaction object. 134 | */ 135 | module.exports.setManagementProxy = internal.setManagementProxy; 136 | 137 | /** 138 | * Configure a point with Urbit public keys, incrementing the point's continuity 139 | * number if needed. 140 | * @param {Object} contracts - An Urbit contracts object. 141 | * @param {Number} point - Point number. 142 | * @param {String} encryptionKey - The encryption key. 143 | * @param {String} authenticationKey - The auth key. 144 | * @param {Number} cryptoSuiteVersion - The crypto suite version. 145 | * @param {Bool} discontinuous - True to increment the continuity number. 146 | * @return {Object} An unsigned transaction object. 147 | */ 148 | module.exports.configureKeys = internal.configureKeys; 149 | 150 | 151 | /** 152 | * Spawn a point, giving ownership of it to the target address. 153 | * @param {Object} contracts - An Urbit contracts object. 154 | * @param {Number} point - A point number. 155 | * @param {String} target - The target address. 156 | * @return {Object} An unsigned transaction object. 157 | */ 158 | module.exports.spawn = internal.spawn; 159 | 160 | /** 161 | * Give an address the right to spawn points with the given prefix. 162 | * @param {Object} contracts - An Urbit contracts object. 163 | * @param {Number} prefix - A (prefix) point number. 164 | * @param {String} address - The address to designate as a spawn proxy. 165 | * @return {Object} An unsigned transaction object. 166 | */ 167 | module.exports.setSpawnProxy = internal.setSpawnProxy; 168 | 169 | /** 170 | * Transfer a point to a target address, optionally clearing all permissions 171 | * data and keys. 172 | * @param {Object} contracts - An Urbit contracts object. 173 | * @param {Number} point - Point number. 174 | * @param {String} address - The target address. 175 | * @param {Bool} reset - True to reset point's keys. 176 | * @return {Object} An unsigned transaction object. 177 | */ 178 | module.exports.transferPoint = internal.transferPoint; 179 | 180 | /** 181 | * Give an address the right to transfer the given point. 182 | * @param {Object} contracts - An Urbit contracts object. 183 | * @param {Number} prefix - Point number. 184 | * @param {String} address - The address to designate as a transfer proxy. 185 | * @return {Object} An unsigned transaction object. 186 | */ 187 | module.exports.setTransferProxy = internal.setTransferProxy; 188 | 189 | /** 190 | * Request escape from 'point' to 'sponsor'. 191 | * @param {Object} contracts - An Urbit contracts object. 192 | * @param {Number} point - Escapee's point number. 193 | * @param {Number} sponsor - Sponsor's point number. 194 | * @return {Object} An unsigned transaction object. 195 | */ 196 | module.exports.escape = internal.escape; 197 | 198 | /** 199 | * Cancel the currently set escape for a point. 200 | * @param {Object} contracts - An Urbit contracts object. 201 | * @param {Number} point - Escapee's point number. 202 | * @return {Object} An unsigned transaction object. 203 | */ 204 | module.exports.cancelEscape = internal.cancelEscape; 205 | 206 | /** 207 | * As the sponsor, accept the escapee. 208 | * @param {Object} contracts - An Urbit contracts object. 209 | * @param {Number} escapee - Escapee's point number. 210 | * @return {Object} An unsigned transaction object. 211 | */ 212 | module.exports.adopt = internal.adopt; 213 | 214 | /** 215 | * As the sponsor, reject the escapee's escape request. 216 | * @param {Object} contracts - An Urbit contracts object. 217 | * @param {Number} escapee - Escapee's point number. 218 | * @return {Object} An unsigned transaction object. 219 | */ 220 | module.exports.reject = internal.reject; 221 | 222 | /** 223 | * As the sponsor, stop sponsoring the point. 224 | * @param {Object} contracts - An Urbit contracts object. 225 | * @param {Number} point - Point number. 226 | * @return {Object} An unsigned transaction object. 227 | */ 228 | module.exports.detach = internal.detach; 229 | 230 | /** 231 | * Configure the voting proxy address for the galaxy. 232 | * @param {Object} contracts - An Urbit contracts object. 233 | * @param {Number} galaxy - Point number. 234 | * @param {String} proxy - The proxy's address. 235 | * @return {Object} An unsigned transaction object. 236 | */ 237 | module.exports.setVotingProxy = internal.setVotingProxy; 238 | 239 | /** 240 | * As a galaxy, start a poll for the ecliptic upgrade proposal. 241 | * @param {Object} contracts - An Urbit contracts object. 242 | * @param {Number} galaxy - A (galaxy) point number. 243 | * @param {Object} proposal - The ecliptic upgrade proposal. 244 | * @return {Object} An unsigned transaction object. 245 | */ 246 | module.exports.startUpgradePoll = internal.startUpgradePoll; 247 | 248 | /** 249 | * As a galaxy, start a poll for a proposal. 250 | * @param {Object} contracts - An Urbit contracts object. 251 | * @param {Number} galaxy - A (galaxy) point number. 252 | * @param {String} proposal - The proposal document. 253 | * @return {Object} An unsigned transaction object. 254 | */ 255 | module.exports.startDocumentPoll = internal.startDocumentPoll; 256 | 257 | /** 258 | * As a galaxy, cast a vote on the ecliptic upgrade proposal. 259 | * @param {Object} contracts - An Urbit contracts object. 260 | * @param {Number} galaxy - A (galaxy) point number. 261 | * @param {Object} proposal - The upgrade proposal. 262 | * @param {Bool} vote - True if yes, false otherwise. 263 | * @return {Object} An unsigned transaction object. 264 | */ 265 | module.exports.castUpgradeVote = internal.castUpgradeVote; 266 | 267 | /** 268 | * As a galaxy, cast a vote on the proposal. 269 | * @param {Object} contracts - An Urbit contracts object. 270 | * @param {Number} galaxy - A (galaxy) point number. 271 | * @param {String} proposal - The proposal document. 272 | * @param {Bool} vote - True if yes, false otherwise. 273 | * @return {Object} An unsigned transaction object. 274 | */ 275 | module.exports.castDocumentVote = internal.castDocumentVote; 276 | 277 | /** 278 | * Check whether the proposal has achieved majority, upgrading to it if so. 279 | * @param {Object} contracts - An Urbit contracts object. 280 | * @param {Object} proposal - The upgrade proposal. 281 | * @return {Object} An unsigned transaction object. 282 | */ 283 | module.exports.updateUpgradePoll = internal.updateUpgradePoll; 284 | 285 | /** 286 | * Check whether the proposal has achieved majority. 287 | * @param {Object} contracts - An Urbit contracts object. 288 | * @param {Object} proposal - The proposal document. 289 | * @return {Object} An unsigned transaction object. 290 | */ 291 | module.exports.updateDocumentPoll = internal.updateDocumentPoll; 292 | 293 | /** 294 | * Grant the target address ownership of the galaxy and register it for voting. 295 | * @param {Object} contracts - An Urbit contracts object. 296 | * @param {Number} galaxy - A (galaxy) point number. 297 | * @param {String} target - The target address. 298 | * @return {Object} An unsigned transaction object. 299 | */ 300 | module.exports.createGalaxy = internal.createGalaxy; 301 | 302 | /** 303 | * Set primary, secondary, adn tertiary DNS domains for the ecliptic. 304 | * @param {Object} contracts - An Urbit contracts object. 305 | * @param {String} primary - Primary DNS address. 306 | * @param {String} secondary - Secondary DNS address. 307 | * @param {String} tertiary - Tertiary DNS address. 308 | * @return {Object} An unsigned transaction object. 309 | */ 310 | module.exports.setDnsDomains = internal.setDnsDomains; 311 | -------------------------------------------------------------------------------- /azimuth.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Azimuth API 3 | * @module azimuth 4 | */ 5 | 6 | const internal = require('./internal/azimuth'); 7 | const utils = require('./utils'); 8 | 9 | // Generic API for azimuth 10 | // 11 | // Typically: 12 | // 13 | // * if 'point' is an object, will compute locally 14 | // * if 'point' is a uint, will hit the network 15 | // 16 | // Note that the type check for 'object' is weak, but intentionally so: the 17 | // type branch is intended to provide a generic API, and to work seamlessly 18 | // with Promises, rather than to prevent invalid inputs *per se*. 19 | 20 | /** 21 | * The Azimuth contract's address on the Ethereum mainnet. 22 | * 23 | * Unlike that of the Ecliptic, Polls, etc. contracts, the Azimuth contract's 24 | * address never changes. It is also registered as 'azimuth.eth' on ENS. 25 | */ 26 | const mainnet = '0x223c067f8cf28ae173ee5cafea60ca44c335fecb' 27 | 28 | /** 29 | * Check if an address is the owner of a point. 30 | * @param {Object} contracts - An Urbit contracts object. 31 | * @param {Number | Object} point - Point number or point object. 32 | * @param {Number} address - Owner's address. 33 | * @return {Promise} True if owner of the point, false otherwise. 34 | */ 35 | function isOwner(contracts, point, address) { 36 | if (typeof point === 'object') { 37 | return utils.addressEquals(point.owner, address); 38 | } 39 | return internal.isOwner(contracts, point, address); 40 | } 41 | 42 | /** 43 | * Get the owner of a point. 44 | * @param {Object} contracts - An Urbit contracts object. 45 | * @param {Number | Object} point - Point number or point object. 46 | * @return {Promise
} The point's owner. 47 | */ 48 | function getOwner(contracts, point) { 49 | if (typeof point === 'object') { 50 | return point.owner; 51 | } 52 | return internal.getOwner(contracts, point); 53 | } 54 | 55 | /** 56 | * Check if a point is active. 57 | * @param {Object} contracts - An Urbit contracts object. 58 | * @param {Number | Object} point - Point number or point object. 59 | * @return {Promise} True if the point is active, false otherwise. 60 | */ 61 | function isActive(contracts, point) { 62 | if (typeof point === 'object') { 63 | return point.active; 64 | } 65 | return internal.isActive(contracts, point); 66 | } 67 | 68 | /** 69 | * Get the key configuration for a point. 70 | * @param {Object} contracts - An Urbit contracts object. 71 | * @param {Number | Object} point - Point number or point object. 72 | * @return {Promise} The point's key configuration. 73 | */ 74 | function getKeys(contracts, point) { 75 | if (typeof point === 'object') { 76 | return { 77 | encryptionKey: point.encryptionKey, 78 | authenticationKey: point.authenticationKey, 79 | cryptoSuiteVersion: point.cryptoSuiteVersion, 80 | keyRevisionNumber: point.keyRevisionNumber 81 | }; 82 | } 83 | return internal.getKeys(contracts, point); 84 | } 85 | 86 | /** 87 | * Get the key revision number of a point. 88 | * @param {Object} contracts - An Urbit contracts object. 89 | * @param {Number | Object} point - Point number or point object. 90 | * @return {Promise} The point's key revision number. 91 | */ 92 | function getKeyRevisionNumber(contracts, point) { 93 | if (typeof point === 'object') { 94 | return point.keyRevisionNumber; 95 | } 96 | return internal.getKeyRevisionNumber(contracts, point); 97 | } 98 | 99 | /** 100 | * Check if a point has been booted. 101 | * @param {Object} contracts - An Urbit contracts object. 102 | * @param {Number | Object} point - Point number or point object. 103 | * @return {Promise} True if it has been booted, false otherwise. 104 | */ 105 | function hasBeenLinked(contracts, point) { 106 | if (typeof point === 'object') { 107 | return point.keyRevisionNumber > 0; 108 | } 109 | return internal.hasBeenLinked(contracts, point); 110 | } 111 | 112 | /** 113 | * Check if a point is live. 114 | * @param {Object} contracts - An Urbit contracts object. 115 | * @param {Number | Object} point - Point number or point object. 116 | * @return {Promise} True if the point is live, false otherwise. 117 | */ 118 | function isLive(contracts, point) { 119 | if (typeof point === 'object') { 120 | let ekey = point.encryptionKey; 121 | let akey = point.authenticationKey; 122 | let crsv = point.cryptoSuiteVersion; 123 | 124 | return ekey !== 0 && akey !== 0 && crsv !== 0; 125 | } 126 | return internal.isLive(contracts, point); 127 | } 128 | 129 | /** 130 | * Get a point's continuity number. 131 | * @param {Object} contracts - An Urbit contracts object. 132 | * @param {Number | Object} point - Point number or point object. 133 | * @return {Promise} The point's continuity number. 134 | */ 135 | function getContinuityNumber(contracts, point) { 136 | if (typeof point === 'object') { 137 | return point.continuityNumber; 138 | } 139 | return internal.getContinuityNumber(contracts, point); 140 | } 141 | 142 | /** 143 | * Get a point's spawn count. 144 | * @param {Object} contracts - An Urbit contracts object. 145 | * @param {Number} point - Point number. 146 | * @return {Promise} The point's spawn count. 147 | */ 148 | function getSpawnCount(contracts, point) { 149 | return internal.getSpawnCount(contracts, point); 150 | } 151 | 152 | /** 153 | * Get an array of all child points the target point has spawned. 154 | * @param {Object} contracts - An Urbit contracts object. 155 | * @param {Number} point - Point number. 156 | * @return {Promise>} The spawned points. 157 | */ 158 | async function getSpawned(contracts, point) { 159 | //There is an inconsistency in the web3 library: in some versions 160 | // getSpawned returns Array, in others Array, 161 | // so map to Number to be on the safe side. 162 | return (await internal.getSpawned(contracts, point)).map(Number); 163 | } 164 | 165 | /** 166 | * Get a point's sponsor. 167 | * @param {Object} contracts - An Urbit contracts object. 168 | * @param {Number | Object} point - Point number or point object. 169 | * @return {Promise} The point's sponsor. 170 | */ 171 | function getSponsor(contracts, point) { 172 | if (typeof point === 'object') { 173 | return point.sponsor; 174 | } 175 | return internal.getSponsor(contracts, point); 176 | } 177 | 178 | /** 179 | * Get an array of all points the target point is sponsoring. 180 | * @param {Object} contracts - An Urbit contracts object. 181 | * @param {Number} point - Point number. 182 | * @return {Promise>} The points being sponsored. 183 | */ 184 | const getSponsoring = internal.getSponsoring; 185 | 186 | /** 187 | * Get the number of points the target point is sponsoring. 188 | * @param {Object} contracts - An Urbit contracts object. 189 | * @param {Number} point - Point number. 190 | * @return {Promise} The amount of points being sponsored. 191 | */ 192 | const getSponsoringCount = internal.getSponsoringCount; 193 | 194 | /** 195 | * Check if a point has a sponsor. 196 | * @param {Object} contracts - An Urbit contracts object. 197 | * @param {Number | Object} point - Point number or point object. 198 | * @return {Promise} True if the point has a sponsor, false otherwise. 199 | */ 200 | function hasSponsor(contracts, point) { 201 | if (typeof point === 'object') { 202 | return point.hasSponsor; 203 | } 204 | return internal.hasSponsor(contracts, point); 205 | } 206 | 207 | /** 208 | * Check if a point is the sponsor of another. 209 | * @param {Object} contracts - An Urbit contracts object. 210 | * @param {Number | Object} point - Point number or point object. 211 | * @param {Number} sponsor - The sponsor's point number. 212 | * @return {Promise} True if a sponsor, false otherwise. 213 | */ 214 | function isSponsor(contracts, point, sponsor) { 215 | if (typeof point === 'object') { 216 | return point.hasSponsor && point.sponsor === sponsor; 217 | } 218 | return internal.isSponsor(contracts, point, sponsor); 219 | } 220 | 221 | /** 222 | * Check if a point is requesting escape. 223 | * @param {Object} contracts - An Urbit contracts object. 224 | * @param {Number | Object} point - Point number or point object. 225 | * @return {Promise} True if requesting escape, false otherwise. 226 | */ 227 | function isEscaping(contracts, point) { 228 | if (typeof point === 'object') { 229 | return point.escapeRequested; 230 | } 231 | return internal.isEscaping(contracts, point); 232 | } 233 | 234 | /** 235 | * Get the sponsor that another point is requesting escape to. 236 | * @param {Object} contracts - An Urbit contracts object. 237 | * @param {Number | Object} point - Point number or point object. 238 | * @return {Promise} The sponsor point number. 239 | */ 240 | function getEscapeRequest(contracts, point) { 241 | if (typeof point === 'object') { 242 | return point.escapeRequestedTo; 243 | } 244 | return internal.getEscapeRequest(contracts, point); 245 | } 246 | 247 | /** 248 | * Get a list of points that are requesting escape to a sponsor. 249 | * @param {Object} contracts - An Urbit contracts object. 250 | * @param {Number} point - Point number. 251 | * @return {Promise>} An array of points requesting escape 252 | */ 253 | const getEscapeRequests = internal.getEscapeRequests; 254 | 255 | /** 256 | * Get the number of points that are requesting escape to a sponsor. 257 | * @param {Object} contracts - An Urbit contracts object. 258 | * @param {Number} point - Point number. 259 | * @return {Promise} The number of points requesting escape 260 | */ 261 | const getEscapeRequestsCount = internal.getEscapeRequestsCount; 262 | 263 | /** 264 | * Check if a point is requesting escape to another point. 265 | * @param {Object} contracts - An Urbit contracts object. 266 | * @param {Number | Object} point - Point number or point object. 267 | * @param {Number} sponsor - Sponsor's point number. 268 | * @return {Promise} True if escape to sponsor requested, false 269 | * otherwise. 270 | */ 271 | function isRequestingEscapeTo(contracts, point, sponsor) { 272 | if (typeof point === 'object') { 273 | return point.escapeRequested && point.escapeRequestedTo === sponsor; 274 | } 275 | return internal.isRequestingEscapeTo(contracts, point, sponsor); 276 | } 277 | 278 | /** 279 | * Check if an address is a spawn proxy for a point. 280 | * @param {Object} contracts - An Urbit contracts object. 281 | * @param {Number | Object} point - Point number or point object. 282 | * @param {String} address - Target address. 283 | * @return {Promise} True if address is spawn proxy, false otherwise. 284 | */ 285 | function isSpawnProxy(contracts, point, address) { 286 | if (typeof point === 'object') { 287 | return utils.addressEquals(point.spawnProxy, address); 288 | } 289 | return internal.isSpawnProxy(contracts, point, address); 290 | } 291 | 292 | /** 293 | * Get the spawn proxy for a point. 294 | * @param {Object} contracts - An Urbit contracts object. 295 | * @param {Number | Object} point - Point number or point object. 296 | * @return {Promise} The spawn proxy's address. 297 | */ 298 | function getSpawnProxy(contracts, point) { 299 | if (typeof point === 'object') { 300 | return point.spawnProxy; 301 | } 302 | return internal.getSpawnProxy(contracts, point); 303 | } 304 | 305 | /** 306 | * Check if an address is a transfer proxy for a point. 307 | * @param {Object} contracts - An Urbit contracts object. 308 | * @param {Number | Object} point - Point number or point object. 309 | * @param {String} address - Target address. 310 | * @return {Promise} True if the address is a transfer proxy, false 311 | * otherwise. 312 | */ 313 | function isTransferProxy(contracts, point, address) { 314 | if (typeof point === 'object') { 315 | return utils.addressEquals(point.transferProxy, address); 316 | } 317 | return internal.isTransferProxy(contracts, point, address); 318 | } 319 | 320 | /** 321 | * Get the transfer proxy for a point. 322 | * @param {Object} contracts - An Urbit contracts object. 323 | * @param {Number | Object} point - Point number or point object. 324 | * @return {Promise} The transfer proxy's address. 325 | */ 326 | function getTransferProxy(contracts, point) { 327 | if (typeof point === 'object') { 328 | return point.transferProxy; 329 | } 330 | return internal.getTransferProxy(contracts, point); 331 | } 332 | 333 | // NB (jtobin): 334 | // 335 | // The following do not work with cached point types, and AFAICT can not be 336 | // made to. 337 | 338 | /** 339 | * Calculate the prefix of a point. 340 | * @param {Number} point - Point number. 341 | * @return {Number} The point's prefix. 342 | */ 343 | function getPrefix(point) { 344 | if (point < 65536) { return point % 256; } 345 | return point % 65536; 346 | } 347 | 348 | let PointSize = { 349 | Galaxy: 0, 350 | Star: 1, 351 | Planet: 2 352 | } 353 | 354 | /** 355 | * Calculate the size of a point. 356 | * @param {Number} point - Point number. 357 | * @return {Number} The point's size. 358 | */ 359 | function getPointSize(point) { 360 | if (point < 256) { return PointSize.Galaxy; } 361 | if (point < 65536) { return PointSize.Star; } 362 | return PointSize.Planet; 363 | } 364 | 365 | /** 366 | * Get the azimuth contract owner. 367 | * @param {Object} contracts - An Urbit contracts object. 368 | * @return {Promise} The contract owner's address. 369 | */ 370 | const owner = internal.owner; 371 | 372 | /** 373 | * Get a point object, given its point id. 374 | * @param {Object} contracts - An Urbit contracts object. 375 | * @param {Number} point - Point number. 376 | * @param {string} what - 'state', 'rights', defaults to 'both' 377 | * @return {Promise} A point object with the requested data. 378 | */ 379 | async function getPoint(contracts, point, what) { 380 | what = what || 'both'; 381 | let data = {}; 382 | if (what === 'both' || what === 'state') { 383 | data = await internal.getPoint(contracts, point); 384 | } 385 | if (what === 'both' || what === 'rights') { 386 | Object.assign(data, await internal.getRights(contracts, point)); 387 | } 388 | return data; 389 | } 390 | 391 | /** 392 | * Get a list of unspawned/spawnable points 393 | * @param {Object} contracts - An Urbit contracts object. 394 | * @param {Number} point - Point number. 395 | * @return {Promise>} - Unspawned children of point 396 | */ 397 | async function getUnspawnedChildren(contracts, point) { 398 | let size = getPointSize(point); 399 | if (size >= PointSize.Planet) { 400 | return []; 401 | } 402 | let spawned = await getSpawned(contracts, point); 403 | let unspawned = []; 404 | let childSpace = (size === PointSize.Galaxy) ? 0x100 : 0x10000; 405 | for (let i = 1; i < childSpace; i++) { 406 | let child = point + (i*childSpace); 407 | if (spawned.indexOf(child) < 0) { 408 | unspawned.push(child); 409 | } 410 | } 411 | return unspawned; 412 | } 413 | 414 | /** 415 | * Get the block at which the point was activated. Returns zero if it hasn't 416 | * been activated yet. 417 | * @param {Object} contracts - An Urbit contracts object. 418 | * @param {Number} point - Point number. 419 | * @param {Number} minBlock - (optional) Block to start search at. (Default 0.) 420 | * @param {Number} maxBlock - (optional) Block to end search at. (Default latest.) 421 | * @return {Promise} - Block of activation. 422 | */ 423 | async function getActivationBlock(contracts, point, minBlock, maxBlock) { 424 | minBlock = minBlock || 0; 425 | maxBlock = maxBlock || 'latest'; 426 | const logs = await contracts.azimuth.getPastEvents('Activated', { 427 | fromBlock: minBlock, 428 | toBlock: maxBlock, 429 | filter: { point: [point] }, 430 | }); 431 | if (logs.length === 0) { 432 | return 0; 433 | } else { 434 | return logs[0].blockNumber; 435 | } 436 | } 437 | 438 | /** 439 | * Get the points that an address owns. 440 | * @param {Object} contracts - An Urbit contracts object. 441 | * @param {String} address - The target address. 442 | * @return {Promise>} An array of owned azimuth. 443 | */ 444 | const getOwnedPoints = internal.getOwnedPoints; 445 | 446 | /** 447 | * Get a count of points owned by an address. 448 | * @param {Object} contracts - An Urbit contracts object. 449 | * @param {String} address - The target address. 450 | * @return {Promise} Owned point count for the address. 451 | */ 452 | const getOwnedPointCount = internal.getOwnedPointCount; 453 | 454 | /** 455 | * Get the point at the given index of the array containing an owner's azimuth. 456 | * @param {Object} contracts - An Urbit contracts object. 457 | * @param {String} address - The target address. 458 | * @param {Number} index - The index of the array. 459 | * @return {Promise} The point at the provided index. 460 | */ 461 | const getOwnedPointAtIndex = internal.getOwnedPointAtIndex; 462 | 463 | /** 464 | * Check if an address is a management proxy for a point. 465 | * @param {Object} contracts - An Urbit contracts object. 466 | * @param {Number | Object} point - Point number or point object. 467 | * @param {String} address - Target address. 468 | * @return {Promise} True if address is management proxy, false otherwise. 469 | */ 470 | function isManagementProxy(contracts, point, address) { 471 | if (typeof point === 'object') { 472 | return utils.addressEquals(point.managementProxy, address); 473 | } 474 | return internal.isManagementProxy(contracts, point, address); 475 | } 476 | 477 | /** 478 | * Get the management proxy for a point. 479 | * @param {Object} contracts - An Urbit contracts object. 480 | * @param {Number | Object} point - Point number or point object. 481 | * @return {Promise} The management proxy's address. 482 | */ 483 | function getManagementProxy(contracts, point) { 484 | if (typeof point === 'object') { 485 | return point.managementProxy; 486 | } 487 | return internal.getManagementProxy(contracts, point); 488 | } 489 | 490 | 491 | /** 492 | * Check if an address can manage a point. 493 | * @param {Object} contracts - An Urbit contracts object. 494 | * @param {Number} point - Point number. 495 | * @param {String} address - The manager's address. 496 | * @return {Promise} True if the address can manage the point, false 497 | * otherwise. 498 | */ 499 | const canManage = internal.canManage; 500 | 501 | /** 502 | * Get a count of the points an address is managing. 503 | * @param {Object} contracts - An Urbit contracts object. 504 | * @param {String} address - The target address. 505 | * @return {Promise} The count of points being managed. 506 | */ 507 | const getManagerForCount = internal.getManagerForCount; 508 | 509 | /** 510 | * Get the points an account is managing. 511 | * @param {Object} contracts - An Urbit contracts object. 512 | * @param {String} address - The target address. 513 | * @return {Promise>} The points being managed. 514 | */ 515 | const getManagerFor = internal.getManagerFor; 516 | 517 | /** 518 | * Check if an address is a voting proxy for an owner. 519 | * @param {Object} contracts - An Urbit contracts object. 520 | * @param {String} address - The owner's address. 521 | * @param {String} address - The voting proxy's address. 522 | * @return {Promise} True is voting proxy, false otherwise. 523 | */ 524 | const isVotingProxy = internal.isVotingProxy; 525 | 526 | /** 527 | * Check if an address can vote for a point. 528 | * @param {Object} contracts - An Urbit contracts object. 529 | * @param {Number} point - Point number. 530 | * @param {String} address - The target address. 531 | * @return {Promise} True is the address can vote for the point, false 532 | * otherwise. 533 | */ 534 | const canVoteAs = internal.canVoteAs; 535 | 536 | /** 537 | * Get a count of the points an address can vote for. 538 | * @param {Object} contracts - An Urbit contracts object. 539 | * @param {String} address - The target address. 540 | * @return {Promise} The count of points that can be voted for. 541 | */ 542 | const getVotingForCount = internal.getVotingForCount; 543 | 544 | /** 545 | * Get the points an account is voting for. 546 | * @param {Object} contracts - An Urbit contracts object. 547 | * @param {String} address - The target address. 548 | * @return {Promise>} The points being voted for. 549 | */ 550 | const getVotingFor = internal.getVotingFor; 551 | 552 | /** 553 | * Get a count of the points an address is a spawn proxy for. 554 | * @param {Object} contracts - An Urbit contracts object. 555 | * @param {String} address - The target address. 556 | * @return {Promise} The count of azimuth. 557 | */ 558 | const getSpawningForCount = internal.getSpawningForCount; 559 | 560 | /** 561 | * Get the points an account is a spawn proxy for. 562 | * @param {Object} contracts - An Urbit contracts object. 563 | * @param {String} address - The target address. 564 | * @return {Promise>} The azimuth. 565 | */ 566 | const getSpawningFor = internal.getSpawningFor; 567 | 568 | /** 569 | * Get a count of the points an address is a transfer proxy for. 570 | * @param {Object} contracts - An Urbit contracts object. 571 | * @param {String} address - The target address. 572 | * @return {Promise} The count of azimuth. 573 | */ 574 | const getTransferringForCount = internal.getTransferringForCount; 575 | 576 | /** 577 | * Get the points an account is a transfer proxy for. 578 | * @param {Object} contracts - An Urbit contracts object. 579 | * @param {String} address - The target address. 580 | * @return {Promise>} The azimuth. 581 | */ 582 | const getTransferringFor = internal.getTransferringFor; 583 | 584 | /** 585 | * Check if an address is an operator for another. 586 | * @param {Object} contracts - An Urbit contracts object. 587 | * @param {String} address - The owner's address. 588 | * @param {String} address - The operator's address. 589 | * @return {Promise} True is operator, false otherwise. 590 | */ 591 | const isOperator = internal.isOperator; 592 | 593 | module.exports = { 594 | mainnet, 595 | owner, 596 | getPoint, 597 | getOwnedPoints, 598 | getOwnedPointCount, 599 | getOwnedPointAtIndex, 600 | isManagementProxy, 601 | canManage, 602 | getManagerForCount, 603 | getManagerFor, 604 | isVotingProxy, 605 | canVoteAs, 606 | getVotingForCount, 607 | getVotingFor, 608 | isOwner, 609 | getOwner, 610 | isActive, 611 | getKeys, 612 | getKeyRevisionNumber, 613 | hasBeenLinked, 614 | isLive, 615 | getContinuityNumber, 616 | getSpawnCount, 617 | getSpawned, 618 | getUnspawnedChildren, 619 | getActivationBlock, 620 | getSponsor, 621 | getSponsoring, 622 | getSponsoringCount, 623 | hasSponsor, 624 | isSponsor, 625 | isEscaping, 626 | getEscapeRequest, 627 | getEscapeRequests, 628 | getEscapeRequestsCount, 629 | isRequestingEscapeTo, 630 | isSpawnProxy, 631 | getSpawnProxy, 632 | getSpawningForCount, 633 | getSpawningFor, 634 | isTransferProxy, 635 | getTransferProxy, 636 | getTransferringForCount, 637 | getTransferringFor, 638 | getPrefix, 639 | PointSize, 640 | getPointSize, 641 | isOperator 642 | } 643 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('chai').assert; 4 | const bip39 = require('bip39'); 5 | const hdkey = require('hdkey'); 6 | const Web3 = require('web3'); 7 | const ethUtil = require('ethereumjs-util'); 8 | 9 | const ajs = require('..'); 10 | const check = ajs.check; 11 | const ecliptic = ajs.ecliptic; 12 | const azimuth = ajs.azimuth; 13 | const delsend = ajs.delegatedSending; 14 | const details = ajs.chainDetails; 15 | const txn = ajs.txn; 16 | const claims = ajs.claims; 17 | 18 | const reasons = require('../resources/reasons.json'); 19 | 20 | // accounts 21 | 22 | const mnemonic = 'benefit crew supreme gesture quantum web media hazard theory mercy wing kitten'; 23 | 24 | const seed = bip39.mnemonicToSeedSync(mnemonic); 25 | 26 | const hd = hdkey.fromMasterSeed(seed); 27 | 28 | const path = "m/44'/60'/0'/0"; 29 | 30 | const getKeyPair = (hd, path, index) => { 31 | let derived = hd.derive(path + '/' + index); 32 | let sk = derived.privateKey; 33 | let address = ethUtil.privateToAddress(sk); 34 | return { 35 | address: address, 36 | privateKey: sk 37 | }; 38 | } 39 | 40 | const pair0 = getKeyPair(hd, path, 0); 41 | const pair1 = getKeyPair(hd, path, 1); 42 | const pair2 = getKeyPair(hd, path, 2); 43 | 44 | const ac0 = ethUtil.addHexPrefix(pair0.address.toString('hex')); 45 | const ac1 = ethUtil.addHexPrefix(pair1.address.toString('hex')); 46 | const ac2 = ethUtil.addHexPrefix(pair2.address.toString('hex')); 47 | 48 | const pk0 = pair0.privateKey; 49 | const pk1 = pair1.privateKey; 50 | const pk2 = pair2.privateKey; 51 | 52 | const zaddr = ethUtil.zeroAddress(); 53 | 54 | // contract addresses 55 | 56 | const contractAddresses = { 57 | ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480', 58 | azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381', 59 | polls: '0x935452c45eda2958976a429c9733c40302995efd', 60 | claims: '0xe0834579269eac6beca2882a6a21f6fb0b1d7196', 61 | delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43' 62 | } 63 | 64 | // helpers 65 | 66 | function can(res) { 67 | return assert.isTrue(res.result, res.reason); 68 | } 69 | 70 | function cant(res, reason) { 71 | assert.isFalse(res.result); 72 | return assert.equal(res.reason, reason); 73 | } 74 | 75 | function renderAsHex(value) { 76 | return ethUtil.addHexPrefix(value.toString('hex')); 77 | } 78 | 79 | async function firstUnownedGalaxy(contracts) { 80 | let galaxy = 0; 81 | while (await check.hasOwner(contracts, galaxy)) galaxy++; 82 | return galaxy; 83 | } 84 | 85 | async function sendTransaction(web3, tx, privateKey) { 86 | if (!ethUtil.isValidPrivate(privateKey)) { 87 | throw "Invalid key"; 88 | } 89 | 90 | let addr = ethUtil.privateToAddress(privateKey); 91 | 92 | // NB (jtobin): 93 | // 94 | // Explicitly set the tx.from field to whoever owns the supplied private 95 | // key. We don't want to depend on the state of web3.eth.defaultAccount, 96 | // implicitly or otherwise, ever. 97 | 98 | tx.from = renderAsHex(addr); 99 | 100 | let stx = await txn.signTransaction(web3, tx, privateKey); 101 | return txn.sendSignedTransaction(web3, stx); 102 | } 103 | 104 | 105 | // tests 106 | 107 | function main() { 108 | 109 | let provider = new Web3.providers.HttpProvider('http://localhost:8545'); 110 | let web3 = new Web3(provider); 111 | let contracts = ajs.initContracts(web3, contractAddresses); 112 | 113 | const someBytes32 = web3.utils.asciiToHex('whatever'); 114 | 115 | let galaxy = 0; 116 | let galaxyPlanet = 65536; 117 | let star1 = 256; 118 | let star2 = 512; 119 | let star3 = 768; 120 | let planet1a = 65792; 121 | let planet1b = 131328; 122 | let planet1c = 196864; 123 | let planet1d = 262400; 124 | 125 | it('prepare the environment', async function() { 126 | this.timeout(20000) 127 | 128 | // NB (jtobin): 129 | // 130 | // The polls tests require that a sufficient number of galaxies have 131 | // been spawned, so we explicitly create ~zod and ~nec below. 132 | 133 | let tx = ecliptic.createGalaxy(contracts, 0, ac0); 134 | await sendTransaction(web3, tx, pk0); 135 | 136 | tx = ecliptic.createGalaxy(contracts, 1, ac0); 137 | await sendTransaction(web3, tx, pk0); 138 | 139 | galaxy = await firstUnownedGalaxy(contracts); 140 | star1 = star1 + galaxy; 141 | star2 = star2 + galaxy; 142 | planet1a = planet1a + galaxy; 143 | planet1b = planet1b + galaxy; 144 | planet1c = planet1c + galaxy; 145 | planet1d = planet1d + galaxy; 146 | }); 147 | 148 | describe('#createGalaxy', async function() { 149 | 150 | it('can only be done by contract owner', async function() { 151 | can(await check.canCreateGalaxy(contracts, galaxy, ac0)); 152 | cant(await check.canCreateGalaxy(contracts, galaxy, ac1), 153 | reasons.permission); 154 | }); 155 | 156 | it('prevents targeting the zero address', async function() { 157 | cant(await check.canCreateGalaxy(contracts, galaxy, zaddr), reasons.zero); 158 | }); 159 | 160 | it('generates usable transaction', async function() { 161 | assert.isFalse(await azimuth.isOwner(contracts, galaxy, ac0)); 162 | 163 | let tx = ecliptic.createGalaxy(contracts, galaxy, ac0); 164 | await sendTransaction(web3, tx, pk0); 165 | 166 | assert.isTrue(await azimuth.isOwner(contracts, galaxy, ac0)); 167 | }); 168 | 169 | it('prevents creating existing galaxies', async function() { 170 | cant(await check.canCreateGalaxy(contracts, galaxy, ac0), reasons.spawned); 171 | }); 172 | 173 | }); 174 | 175 | describe('#setManagementProxy', async function() { 176 | 177 | it('can only be done by owner', async function() { 178 | assert.isFalse(await azimuth.canManage(contracts, galaxy, ac2)); 179 | cant(await check.canSetManagementProxy(contracts, galaxy, ac1), 180 | reasons.permission); 181 | can(await check.canSetManagementProxy(contracts, galaxy, ac0)); 182 | }); 183 | 184 | it('generates usable transaction', async function() { 185 | let tx = ecliptic.setManagementProxy(contracts, galaxy, ac2); 186 | await sendTransaction(web3, tx, pk0); 187 | 188 | assert.isTrue(await azimuth.canManage(contracts, galaxy, ac2)); 189 | }); 190 | 191 | }); 192 | 193 | describe('#setVotingProxy', async function() { 194 | 195 | it('can only be done by owner of galaxy', async function() { 196 | assert.isFalse(await azimuth.canVoteAs(contracts, galaxy, ac2)); 197 | cant(await check.canSetVotingProxy(contracts, galaxy, ac1), 198 | reasons.permission); 199 | cant(await check.canSetVotingProxy(contracts, star1, ac0), 200 | reasons.notGalaxy); 201 | can(await check.canSetVotingProxy(contracts, galaxy, ac0)); 202 | }); 203 | 204 | it('generates usable transaction', async function() { 205 | let tx = ecliptic.setVotingProxy(contracts, galaxy, ac2); 206 | await sendTransaction(web3, tx, pk0); 207 | 208 | assert.isTrue(await azimuth.canVoteAs(contracts, galaxy, ac2)); 209 | 210 | tx = ecliptic.setVotingProxy(contracts, galaxy, ac0); 211 | await sendTransaction(web3, tx, pk0); 212 | }); 213 | 214 | }); 215 | 216 | describe('#spawn', async function() { 217 | 218 | it('cannot spawn from unbooted point', async function() { 219 | cant(await check.canSpawn(contracts, star1, ac0), reasons.spawnPrefix); 220 | }); 221 | 222 | it('cannot spawn if not prefix owner (or spawn proxy)', async function() { 223 | cant(await check.canSpawn(contracts, star1, ac1), reasons.permission); 224 | }); 225 | 226 | it('cannot spawn galaxy planets', async function() { 227 | cant(await check.canSpawn(contracts, galaxyPlanet, ac0), 228 | reasons.spawnSize); 229 | }); 230 | 231 | it('can spawn child to self, directly', async function() { 232 | let tx = ecliptic.configureKeys( 233 | contracts, galaxy, someBytes32, someBytes32, 1, false); 234 | await sendTransaction(web3, tx, pk0); 235 | 236 | can(await check.canSpawn(contracts, star1, ac0)); 237 | }); 238 | 239 | it('generates usable transaction', async function() { 240 | this.timeout(10000) // this one can take awhile 241 | 242 | assert.isFalse(await azimuth.isOwner(contracts, star1, ac0)); 243 | assert.isFalse(await azimuth.isActive(contracts, star1)); 244 | assert.equal((await azimuth.getUnspawnedChildren(contracts, galaxy)).length, 255); 245 | 246 | let tx = ecliptic.spawn(contracts, star1, ac0); 247 | await sendTransaction(web3, tx, pk0); 248 | 249 | tx = ecliptic.configureKeys( 250 | contracts, star1, someBytes32, someBytes32, 1, false); 251 | await sendTransaction(web3, tx, pk0); 252 | 253 | assert.isTrue(await azimuth.isOwner(contracts, star1, ac0)); 254 | assert.isTrue(await azimuth.isActive(contracts, star1)); 255 | assert.isFalse(await azimuth.isOwner(contracts, star2, ac0)); 256 | assert.isFalse(await azimuth.isActive(contracts, star2)); 257 | 258 | tx = ecliptic.spawn(contracts, star2, ac1); 259 | await sendTransaction(web3, tx, pk0); 260 | 261 | assert.isTrue(await azimuth.isOwner(contracts, star2, ac0)); 262 | assert.isFalse(await azimuth.isActive(contracts, star2)); 263 | assert.isTrue(await azimuth.isTransferProxy(contracts, star2, ac1)); 264 | assert.equal((await azimuth.getUnspawnedChildren(contracts, galaxy)).length, 253); 265 | }); 266 | 267 | it('prevents spawning spawned points', async function() { 268 | cant(await check.canSpawn(contracts, star2, ac0), reasons.spawned); 269 | }); 270 | 271 | it('prevents targeting the zero address', async function() { 272 | cant(await check.canSpawn(contracts, star2, zaddr), reasons.zero); 273 | }); 274 | 275 | }); 276 | 277 | describe('#setSpawnProxy', async function() { 278 | 279 | it('can only be done by owner or operator', async function() { 280 | cant(await check.canSetSpawnProxy(contracts, galaxy, ac1), 281 | reasons.permission); 282 | can(await check.canSetSpawnProxy(contracts, galaxy, ac0)); 283 | }); 284 | 285 | it('generates usable transaction', async function() { 286 | assert.isFalse(await azimuth.isSpawnProxy(contracts, galaxy, ac1)); 287 | 288 | let tx = ecliptic.setSpawnProxy(contracts, galaxy, ac1); 289 | await sendTransaction(web3, tx, pk0); 290 | 291 | assert.isTrue(await azimuth.isSpawnProxy(contracts, galaxy, ac1)); 292 | }); 293 | 294 | }); 295 | 296 | describe('#transferPoint', async function() { 297 | 298 | it('can only be done by owner/operator/transfer proxy', async function(){ 299 | cant(await check.canTransferPoint(contracts, star2, ac2, ac1), 300 | reasons.permission); 301 | can(await check.canTransferPoint(contracts, star2, ac1, ac1)); 302 | can(await check.canTransferPoint(contracts, star2, ac0, ac1)); 303 | }); 304 | 305 | it('prevents targeting the zero address', async function() { 306 | cant(await check.canTransferPoint(contracts, star2, ac0, zaddr), 307 | reasons.zero); 308 | }); 309 | 310 | it('generates usable transaction', async function() { 311 | assert.isFalse(await azimuth.isOwner(contracts, star2, ac1)); 312 | 313 | let tx = ecliptic.transferPoint(contracts, star2, ac1); 314 | await sendTransaction(web3, tx, pk0); 315 | 316 | assert.isTrue(await azimuth.isOwner(contracts, star2, ac1)); 317 | }); 318 | 319 | }); 320 | 321 | describe('#setTransferProxy', async function() { 322 | 323 | it('can only be done by owner', async function() { 324 | cant(await check.canSetTransferProxy(contracts, galaxy, ac1), 325 | reasons.permission); 326 | can(await check.canSetTransferProxy(contracts, galaxy, ac0)); 327 | }); 328 | 329 | it('generates usable transaction', async function() { 330 | assert.isFalse(await azimuth.isTransferProxy(contracts, galaxy, ac1)); 331 | 332 | let tx = ecliptic.setTransferProxy(contracts, galaxy, ac1); 333 | await sendTransaction(web3, tx, pk0); 334 | 335 | assert.isTrue(await azimuth.isTransferProxy(contracts, galaxy, ac1)); 336 | }); 337 | 338 | }); 339 | 340 | describe('#setManagementProxy', async function() { 341 | 342 | it('generates usable transaction, also configureKeys', async function() { 343 | cant(await check.canConfigureKeys(contracts, galaxy, ac1), 344 | reasons.permission); 345 | 346 | let tx = ecliptic.setManagementProxy(contracts, galaxy, ac1); 347 | await sendTransaction(web3, tx, pk0); 348 | 349 | can(await check.canConfigureKeys(contracts, galaxy, ac1)); 350 | assert.isTrue(await azimuth.isManagementProxy(contracts, galaxy, ac1)); 351 | }); 352 | 353 | }); 354 | 355 | describe('#escape', async function() { 356 | 357 | it('prevents invalid sponsors', async function() { 358 | cant(await check.canEscape(contracts, planet1a, star1, ac0), 359 | reasons.permission); 360 | cant(await check.canEscape(contracts, star1, planet1a, ac0), 361 | reasons.sponsor); 362 | cant(await check.canEscape(contracts, star1, galaxy + 1, ac0), 363 | reasons.sponsorBoot); 364 | can(await check.canEscape(contracts, star1, galaxy, ac0)); 365 | }); 366 | 367 | it('generates usable transaction', async function() { 368 | assert.isFalse(await azimuth.isEscaping(contracts, star2)); 369 | can(await check.canEscape(contracts, star2, star1, ac1)); 370 | 371 | let tx = ecliptic.escape(contracts, star2, star1); 372 | await sendTransaction(web3, tx, pk1); 373 | 374 | assert.isTrue(await azimuth.isEscaping(contracts, star2)); 375 | }); 376 | 377 | }); 378 | 379 | describe('#cancelEscape', async function() { 380 | 381 | it('can only be done by active point manager', async function() { 382 | cant(await check.checkActivePointManager(contracts, planet1a, ac1), 383 | reasons.permission); 384 | can(await check.checkActivePointManager(contracts, star2, ac1)); 385 | }); 386 | 387 | it('generates usable transaction', async function() { 388 | let tx = ecliptic.cancelEscape(contracts, star2); 389 | await sendTransaction(web3, tx, pk1); 390 | 391 | assert.isFalse(await azimuth.isEscaping(contracts, star2)); 392 | }); 393 | 394 | }); 395 | 396 | describe('#adopt', async function() { 397 | 398 | it('can only be done for actual escapees', async function() { 399 | cant(await check.canAdopt(contracts, star2, ac0), 400 | reasons.notEscape); 401 | 402 | let tx = ecliptic.escape(contracts, star2, star1); 403 | await sendTransaction(web3, tx, pk1); 404 | 405 | can(await check.canAdopt(contracts, star2, ac0)); 406 | }); 407 | 408 | it('generates usable transaction', async function() { 409 | let sponsor = (await azimuth.getPoint(contracts, star2)).sponsor; 410 | assert.notEqual(sponsor, star1); 411 | 412 | let tx = ecliptic.adopt(contracts, star2); 413 | await sendTransaction(web3, tx, pk0); 414 | 415 | sponsor = (await azimuth.getPoint(contracts, star2)).sponsor; 416 | assert.equal(sponsor, star1); 417 | }); 418 | 419 | }); 420 | 421 | describe('#reject', async function() { 422 | 423 | it('can only be done for actual escapees', async function() { 424 | cant(await check.canReject(contracts, star2, ac1), 425 | reasons.notEscape); 426 | 427 | let tx = ecliptic.escape(contracts, star2, galaxy); 428 | await sendTransaction(web3, tx, pk1); 429 | 430 | can(await check.canReject(contracts, star2, ac1)); 431 | }); 432 | 433 | it('generates usable transaction', async function() { 434 | assert.isTrue(await azimuth.isEscaping(contracts, star2)); 435 | 436 | let tx = ecliptic.reject(contracts, star2); 437 | await sendTransaction(web3, tx, pk1); 438 | 439 | assert.isFalse(await azimuth.isEscaping(contracts, star2)); 440 | }); 441 | }); 442 | 443 | describe('#detach', async function() { 444 | 445 | it('can only be done by the sponsor', async function() { 446 | cant(await check.canDetach(contracts, star2, ac1), 447 | reasons.permission); 448 | can(await check.canDetach(contracts, star2, ac0)); 449 | }); 450 | 451 | it('generates usable transaction', async function() { 452 | assert.isTrue((await azimuth.getPoint(contracts, star2)).hasSponsor); 453 | 454 | let tx = ecliptic.detach(contracts, star2); 455 | await sendTransaction(web3, tx, pk0); 456 | 457 | assert.isFalse((await azimuth.getPoint(contracts, star2)).hasSponsor); 458 | 459 | cant(await check.canDetach(contracts, star2, ac1), reasons.sponsorless); 460 | }); 461 | 462 | }); 463 | 464 | describe('#polls', async function() { 465 | it('cannot be done by non-voters', async function() { 466 | cant(await check.canStartUpgradePoll(web3, contracts, star1), 467 | reasons.notGalaxy); 468 | 469 | cant(await check.canStartUpgradePoll(web3, contracts, galaxy, zaddr, ac1), 470 | reasons.permission); 471 | 472 | cant(await check.canStartDocumentPoll(contracts, star1), 473 | reasons.notGalaxy); 474 | 475 | cant(await check.canStartDocumentPoll(contracts, galaxy, zaddr, ac1), 476 | reasons.permission); 477 | 478 | cant(await check.canCastUpgradeVote(contracts, star1), 479 | reasons.notGalaxy); 480 | 481 | cant(await check.canCastUpgradeVote(contracts, galaxy, zaddr, ac1), 482 | reasons.permission); 483 | 484 | cant(await check.canCastDocumentVote(contracts, star1), 485 | reasons.notGalaxy); 486 | 487 | cant(await check.canCastDocumentVote(contracts, galaxy, zaddr, ac1), 488 | reasons.permission); 489 | }); 490 | 491 | it('checks for proposal correctness', async function() { 492 | cant(await check.canStartUpgradePoll(web3, contracts, galaxy, ac2, ac0), 493 | reasons.upgradePath); 494 | }); 495 | 496 | it('generates usable transactions', async function() { 497 | this.timeout(10000) // this one can take awhile 498 | 499 | let fakeHash = web3.utils.asciiToHex('poll hash ' + galaxy); 500 | 501 | cant(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0), 502 | reasons.pollInactive); 503 | 504 | await sendTransaction( 505 | web3, 506 | ecliptic.startDocumentPoll(contracts, galaxy, fakeHash), 507 | pk0); 508 | 509 | can(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0)); 510 | 511 | await sendTransaction( 512 | web3, 513 | ecliptic.castDocumentVote(contracts, galaxy, fakeHash, true), 514 | pk0); 515 | 516 | await sendTransaction( 517 | web3, 518 | ecliptic.updateDocumentPoll(contracts, fakeHash), 519 | pk0); 520 | 521 | cant(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0), 522 | reasons.pollVoted); 523 | }); 524 | }); 525 | 526 | describe('#delegatedSending', async function() { 527 | it('sets up for tests', async function() { 528 | let prep = ecliptic.spawn(contracts, planet1c, ac0); 529 | await sendTransaction(web3, prep, pk0); 530 | prep = ecliptic.setSpawnProxy(contracts, star1, contracts.delegatedSending._address); 531 | await sendTransaction(web3, prep, pk0); 532 | prep = ecliptic.setSpawnProxy(contracts, star2, contracts.delegatedSending._address); 533 | await sendTransaction(web3, prep, pk1); 534 | // ac0 now owns planet1c (in addition to star1) 535 | }); 536 | 537 | it('checks for star ownership', async function() { 538 | cant(await check.canSetPoolSize(contracts, planet1c, ac1), 539 | reasons.permission); 540 | can(await check.canSetPoolSize(contracts, planet1c, ac0)); 541 | }); 542 | 543 | it('generates usable transaction', async function() { 544 | assert.equal(await delsend.pools(contracts, planet1c, star1), 0); 545 | 546 | let tx = delsend.setPoolSize(contracts, star1, planet1c, 9); 547 | await sendTransaction(web3, tx, pk0); 548 | tx = delsend.setPoolSize(contracts, star2, planet1c, 1); 549 | await sendTransaction(web3, tx, pk1); 550 | 551 | assert.equal(await delsend.pools(contracts, planet1c, star1), 9); 552 | assert.equal(await delsend.pools(contracts, planet1c, star2), 1); 553 | }); 554 | 555 | it('checks invite send ability', async function() { 556 | cant(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac0, ac0), 557 | reasons.cantReceive); 558 | cant(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac1, ac0), 559 | reasons.cantReceive); 560 | can(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac2, ac0)); 561 | }); 562 | 563 | it('generates usable transaction', async function() { 564 | let tx = delsend.sendPoint(contracts, planet1c, planet1d, ac2); 565 | await sendTransaction(web3, tx, pk0); 566 | 567 | assert.isTrue(await azimuth.isTransferProxy(contracts, planet1d, ac2)); 568 | assert.equal(await delsend.pools(contracts, planet1c, star1), 8); 569 | assert.equal(await delsend.getTotalUsableInvites(contracts, planet1c), 8); 570 | assert.equal(await delsend.invitedBy(contracts, planet1d), planet1c); 571 | assert.equal(await delsend.getPool(contracts, planet1d), planet1c); 572 | let invited = await delsend.getInvited(contracts, planet1c); 573 | assert.equal(invited.length, 1); 574 | assert.equal(invited[0], planet1d); 575 | }); 576 | 577 | it('counts & generates planets to send', async function() { 578 | this.timeout(10000) // this one can take awhile 579 | let shortList = await delsend.getPlanetsToSend(contracts, planet1c, 3); 580 | let longList = await delsend.getPlanetsToSend(contracts, planet1c, 15); 581 | let count = await delsend.getTotalUsableInvites(contracts, planet1c); 582 | assert.equal(shortList.length, 3); 583 | assert.equal(longList.length, 8); 584 | assert.equal(count, 8); 585 | 586 | let tx = ecliptic.configureKeys( 587 | contracts, star2, someBytes32, someBytes32, 1, false 588 | ); 589 | await sendTransaction(web3, tx, pk1); 590 | 591 | longList = await delsend.getPlanetsToSend(contracts, planet1c, 15); 592 | count = await delsend.getTotalUsableInvites(contracts, planet1c); 593 | assert.equal(longList.length, 9); 594 | assert.equal(count, 9); 595 | 596 | tx = ecliptic.setSpawnProxy(contracts, star2, zaddr); 597 | await sendTransaction(web3, tx, pk1); 598 | 599 | longList = await delsend.getPlanetsToSend(contracts, planet1c, 15); 600 | count = await delsend.getTotalUsableInvites(contracts, planet1c); 601 | assert.equal(longList.length, 8); 602 | assert.equal(count, 8); 603 | 604 | }); 605 | }); 606 | 607 | describe('#eventLog', async function() { 608 | 609 | it('can find activated ship block', async function() { 610 | const latest = await web3.eth.getBlockNumber(); 611 | const res = await azimuth.getActivationBlock( 612 | contracts, 613 | star1, 614 | details.local.azimuth.genesis, 615 | latest 616 | ); 617 | assert.notEqual(res, 0); 618 | }); 619 | 620 | it('cannot find unactivated ship block', async function() { 621 | const latest = await web3.eth.getBlockNumber(); 622 | const res = await azimuth.getActivationBlock( 623 | contracts, 624 | star3, 625 | details.local.azimuth.genesis, 626 | latest 627 | ); 628 | assert.equal(res, 0); 629 | }); 630 | }); 631 | 632 | describe('#claims', async function() { 633 | const btcAddress = '1Hz96kJKF2HLPGY15JWLB5m9qGNxvt8tHJ'; 634 | const protocol = 'BTC' 635 | const dossier = '0x00'; 636 | it('can write a claim', async function() { 637 | const newClaimTx = await claims.addClaim(contracts, galaxy, protocol, btcAddress, dossier); 638 | await sendTransaction(web3, newClaimTx, pk0); 639 | 640 | const claim = await claims.getClaim(contracts, galaxy, 0); 641 | 642 | assert.equal(claim.claim, btcAddress); 643 | assert.equal(claim.protocol, protocol); 644 | assert.equal(claim.dossier, dossier); 645 | }); 646 | it('can find all claims', async function() { 647 | const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; 648 | const ethProtocol = 'ETH' 649 | const ethDossier = '0x01'; 650 | const newClaimTx = await claims.addClaim(contracts, galaxy, ethProtocol, ethAddress, ethDossier); 651 | await sendTransaction(web3, newClaimTx, pk0); 652 | 653 | const allClaims = await claims.getAllClaims(contracts, galaxy); 654 | 655 | assert.equal(allClaims.length, 2); 656 | 657 | assert.equal(allClaims[0].claim, btcAddress); 658 | assert.equal(allClaims[0].protocol, protocol); 659 | assert.equal(allClaims[0].dossier, dossier); 660 | 661 | assert.equal(allClaims[1].claim, ethAddress); 662 | assert.equal(allClaims[1].protocol, ethProtocol); 663 | assert.equal(allClaims[1].dossier, ethDossier); 664 | }); 665 | }); 666 | } 667 | 668 | main(); 669 | -------------------------------------------------------------------------------- /check.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Contract checks, assertions, and verifications 3 | * @module check 4 | */ 5 | 6 | const eclipticAbi = require('azimuth-solidity/build/contracts/Ecliptic.json').abi; 7 | const ecliptic = require('./ecliptic'); 8 | const azimuth = require('./azimuth'); 9 | const linearSR = require('./linearSR') 10 | const conditionalSR = require('./conditionalSR') 11 | const polls = require('./polls'); 12 | const delegatedSending = require('./delegatedSending'); 13 | const utils = require('./utils'); 14 | const reasons = require('./resources/reasons.json'); 15 | 16 | const MAXGALAXY = 255; 17 | const MAXSTAR = 65535; 18 | const MAXPLANET = 4294967295; 19 | 20 | /** 21 | * Check if something is a point. 22 | * @param {Number} point - Point number. 23 | * @return {Bool} True if a point, false otherwise. 24 | */ 25 | function isPoint(point) { 26 | return (typeof point === 'number' && point >= 0 && point <= MAXPLANET); 27 | } 28 | 29 | /** 30 | * Check if something is a galaxy. 31 | * @param {Number} point - Point number. 32 | * @return {Bool} True if a galaxy, false otherwise. 33 | */ 34 | function isGalaxy(point) { 35 | return (typeof point === 'number' && point >= 0 && point <= MAXGALAXY); 36 | } 37 | 38 | /** 39 | * Check if something is a star. 40 | * @param {Number} point - Point number. 41 | * @return {Bool} True if a star, false otherwise. 42 | */ 43 | function isStar(point) { 44 | return (typeof point === 'number' && point > MAXGALAXY && point <= MAXSTAR); 45 | } 46 | 47 | /** 48 | * Check if something is a planet. 49 | * @param {Number} point - Point number. 50 | * @return {Bool} True if a planet, false otherwise. 51 | */ 52 | function isPlanet(point) { 53 | return (typeof point === 'number' && point > MAXSTAR && point <= MAXPLANET); 54 | } 55 | 56 | /** 57 | * Check if a point is a prefix of another point. 58 | * @param {Number} point - Point number. 59 | * @return {Bool} True if a prefix, false otherwise. 60 | */ 61 | function isPrefix(point) { 62 | return (typeof point === 'number' && point >= 0 && point <= MAXSTAR); 63 | } 64 | 65 | /** 66 | * Check if a point is a child of another point. 67 | * @param {Number} point - Point number. 68 | * @return {Bool} True if a child, false otherwise. 69 | */ 70 | function isChild(point) { 71 | return (typeof point === 'number' && point > MAXGALAXY && point <= MAXPLANET); 72 | } 73 | 74 | /** 75 | * Check if a poll is active. 76 | * @param {Object} poll - A poll object. 77 | * @return {Bool} True if active, false otherwise. 78 | */ 79 | function pollIsActive(poll) { 80 | let now = Math.round(new Date().getTime()/1000); 81 | let end = poll.start + poll.duration; 82 | return now < end; 83 | } 84 | 85 | /** 86 | * Check if a poll can be started. 87 | * @param {Object} poll - A poll object. 88 | * @return {Bool} True if so, false otherwise. 89 | */ 90 | function canStartPoll(poll) { 91 | let now = Math.round(new Date().getTime()/1000); 92 | let start = poll.start + poll.duration + poll.coolDown; 93 | return now > start; 94 | } 95 | 96 | /** 97 | * Check if a point has an owner. 98 | * @param {Object} contracts - An Urbit contracts object. 99 | * @param {Number} pointId - Point number. 100 | * @return {Promise} True if so, false otherwise. 101 | */ 102 | async function hasOwner(contracts, point) { 103 | if (typeof point === 'object') { 104 | return !utils.addressEquals(point.owner, utils.zeroAddress()); 105 | } 106 | return !await azimuth.isOwner(contracts, point, utils.zeroAddress()); 107 | } 108 | 109 | /** 110 | * Check if an address is the ecliptic owner. 111 | * @param {Object} contracts - An Urbit contracts object. 112 | * @param {String} address - Owner's address. 113 | * @return {Promise} True if so, false otherwise. 114 | */ 115 | async function isEclipticOwner(contracts, address) { 116 | return utils.addressEquals(address, await ecliptic.owner(contracts)); 117 | } 118 | 119 | // NB (jtobin): 120 | // 121 | // Change the { result, reason } objects to use data.either or 122 | // folktale.Result at some point. 123 | 124 | /** 125 | * Check if an address can create the specified galaxy. 126 | * @param {Object} contracts - An Urbit contracts object. 127 | * @param {Number} pointId - Point number. 128 | * @param {String} address - Target address. 129 | * @return {Promise} A result and reason pair. 130 | */ 131 | async function canCreateGalaxy(contracts, galaxy, address) { 132 | let res = { result: false }; 133 | // can't target zero address 134 | if (utils.isZeroAddress(address)) { 135 | res.reason = reasons.zero; 136 | return res; 137 | } 138 | // must be ecliptic owner 139 | if (!await isEclipticOwner(contracts, address)) { 140 | res.reason = reasons.permission; 141 | return res; 142 | } 143 | // can't already exist 144 | if (await hasOwner(contracts, galaxy)) { 145 | res.reason = reasons.spawned; 146 | return res; 147 | } 148 | res.result = true; 149 | return res; 150 | } 151 | 152 | /** 153 | * Check if an address can spawn the given point. 154 | * @param {Object} contracts - An Urbit contracts object. 155 | * @param {Number} pointId - Point number. 156 | * @param {String} address - Target address. 157 | * @return {Promise} A result and reason pair. 158 | */ 159 | async function canSpawn(contracts, point, target) { 160 | let res = { result: false }; 161 | 162 | // prevents targeting the zero address 163 | if (utils.isZeroAddress(target)) { 164 | res.reason = reasons.zero; 165 | return res; 166 | } 167 | let prefix = azimuth.getPrefix(point); 168 | let prefixPointObj = await azimuth.getPoint(contracts, prefix); 169 | 170 | // must either be the owner of the prefixPointObj, or a spawn proxy for it 171 | if (!azimuth.isOwner(contracts, prefixPointObj, target) && 172 | !azimuth.isSpawnProxy(contracts, prefixPointObj, target)) 173 | { 174 | res.reason = reasons.permission; 175 | return res; 176 | } 177 | 178 | // only currently unowned points can be spawned 179 | if (await hasOwner(contracts, point)) { 180 | res.reason = reasons.spawned; 181 | return res; 182 | } 183 | 184 | // only allow spawning of points of the size directly below the prefix 185 | let childSize = azimuth.getPointSize(prefix) + 1; 186 | let pointSize = azimuth.getPointSize(point); 187 | if (childSize !== pointSize) { 188 | res.reason = reasons.spawnSize; 189 | return res; 190 | } 191 | 192 | // prefix must be linked and able to spawn 193 | let ts = Math.round(new Date().getTime() / 1000); 194 | let spawnLimit = await ecliptic.getSpawnLimit(contracts, prefix, ts); 195 | if (!azimuth.hasBeenLinked(contracts, prefixPointObj) || 196 | (await azimuth.getSpawnCount(contracts, point)) >= spawnLimit) 197 | { 198 | res.reason = reasons.spawnPrefix; 199 | return res; 200 | } 201 | 202 | res.result = true 203 | return res; 204 | } 205 | 206 | async function canSetManagementProxy(contracts, point, address) { 207 | return checkActivePointOwner(contracts, point, address); 208 | } 209 | 210 | async function canSetVotingProxy(contracts, point, address) { 211 | if (!isGalaxy(point)) return { result: false, reason: reasons.notGalaxy }; 212 | return checkActivePointOwner(contracts, point, address); 213 | } 214 | 215 | /** 216 | * Check if an address can set a spawn proxy for the given point. 217 | * @param {Object} contracts - An Urbit contracts object. 218 | * @param {Number} pointId - Point number. 219 | * @param {String} address - Target address. 220 | * @return {Promise} A result and reason pair. 221 | */ 222 | async function canSetSpawnProxy(contracts, prefix, address) { 223 | return checkActivePointOwner(contracts, prefix, address); 224 | } 225 | 226 | /** 227 | * Check if the sender address can send the provided point to the target 228 | * address. 229 | * @param {Object} contracts - An Urbit contracts object. 230 | * @param {Number} pointId - Point number. 231 | * @param {String} sender - Sender's address. 232 | * @param {String} target - Target address. 233 | * @return {Promise} A result and reason pair. 234 | */ 235 | async function canTransferPoint(contracts, point, source, target) { 236 | let res = { result: false }; 237 | // prevent burning of points 238 | if (utils.isZeroAddress(target)) 239 | { 240 | res.reason = reasons.zero; 241 | return res; 242 | } 243 | 244 | let pointObj = await azimuth.getPoint(contracts, point); 245 | // must be either the owner of the point, 246 | // a transfer proxy for the point, 247 | // or an operator for the owner 248 | if (!azimuth.isOwner(contracts, pointObj, source) && 249 | !azimuth.isTransferProxy(contracts, pointObj, source) && 250 | !await azimuth.isOperator(contracts, pointObj.owner, source)) 251 | { 252 | res.reason = reasons.permission; 253 | return res; 254 | } 255 | 256 | res.result = true; 257 | return res; 258 | } 259 | 260 | /** 261 | * Check if the address can set a transfer proxy for the point. 262 | * @param {Object} contracts - An Urbit contracts object. 263 | * @param {Number} pointId - Point number. 264 | * @param {String} address - Target address. 265 | * @return {Promise} A result and reason pair. 266 | */ 267 | async function canSetTransferProxy(contracts, point, address) { 268 | let res = { result: false }; 269 | let pointObj = await azimuth.getPoint(contracts, point); 270 | // must be either the owner of the point, 271 | // or an operator for the owner 272 | if (!azimuth.isOwner(contracts, pointObj, address) && 273 | !await azimuth.isOperator(contracts, pointObj.owner, address)) 274 | { 275 | res.reason = reasons.permission; 276 | return res; 277 | } 278 | res.result = true; 279 | return res; 280 | } 281 | 282 | /** 283 | * Check if the target address can make a point escape to the given sponsor. 284 | * @param {Object} contracts - An Urbit contracts object. 285 | * @param {Number} point - Point number. 286 | * @param {Number} sponsor - Point number. 287 | * @param {String} address - Target address. 288 | * @return {Promise} A result and reason pair. 289 | */ 290 | async function canEscape(contracts, point, sponsor, address) { 291 | let asm = await checkActivePointManager(contracts, point, address); 292 | if (!asm.result) { 293 | return asm; 294 | } 295 | let res = { result: false }; 296 | let pointSize = azimuth.getPointSize(point); 297 | let sponsorSize = azimuth.getPointSize(sponsor); 298 | // can only escape to a point one size higher than ourselves, 299 | // except in the special case where the escaping point hasn't 300 | // been booted yet -- in that case we may escape to points of 301 | // the same size, to support lightweight invitation chains. 302 | if (sponsorSize + 1 !== pointSize && 303 | !(sponsorSize === pointSize && 304 | !await azimuth.hasBeenLinked(contracts, point))) 305 | { 306 | res.reason = reasons.sponsor; 307 | return res; 308 | } 309 | // can't escape to a sponsor that hasn't been booted 310 | if (!await azimuth.hasBeenLinked(contracts, sponsor)) 311 | { 312 | res.reason = reasons.sponsorBoot; 313 | return res; 314 | } 315 | res.result = true; 316 | return res; 317 | } 318 | 319 | /** 320 | * Check if a point is active and the target address is its owner. 321 | * @param {Object} contracts - An Urbit contracts object. 322 | * @param {Number} point - Point number. 323 | * @param {String} address - Target address. 324 | * @return {Promise} A result and reason pair. 325 | */ 326 | async function checkActivePointOwner(contracts, point, address) { 327 | let res = { result: false }; 328 | let thePoint = await azimuth.getPoint(contracts, point); 329 | // must be the owner of the point 330 | if (!await azimuth.isOwner(contracts, thePoint, address)) { 331 | res.reason = reasons.permission; 332 | return res; 333 | } 334 | // the point must be active 335 | if (!await azimuth.isActive(contracts, thePoint)) { 336 | res.reason = reasons.inactive; 337 | return res; 338 | } 339 | res.result = true; 340 | return res; 341 | } 342 | 343 | /** 344 | * Check if a point is active and the target address can manage it. 345 | * @param {Object} contracts - An Urbit contracts object. 346 | * @param {Number} point - Point number. 347 | * @param {String} address - Target address. 348 | * @return {Promise} A result and reason pair. 349 | */ 350 | async function checkActivePointManager(contracts, point, address) { 351 | let res = { result: false }; 352 | if (!await azimuth.canManage(contracts, point, address)) 353 | { 354 | res.reason = reasons.permission; 355 | return res; 356 | } 357 | if (!await azimuth.isActive(contracts, point)) 358 | { 359 | res.reason = reasons.inactive; 360 | return res; 361 | } 362 | res.result = true; 363 | return res; 364 | } 365 | 366 | /** 367 | * Check if an address can configure public keys for a point. 368 | * @param {Object} contracts - An Urbit contracts object. 369 | * @param {Number} point - Point number. 370 | * @param {String} address - Target address. 371 | * @return {Promise} A result and reason pair. 372 | */ 373 | async function canConfigureKeys(contracts, point, address) { 374 | return await checkActivePointManager(contracts, point, address); 375 | } 376 | 377 | /** 378 | * Check if the target address can adopt the escapee as its new sponsor. 379 | * @param {Object} contracts - An Urbit contracts object. 380 | * @param {Number} escapee - Escapee's point number. 381 | * @param {String} address - Target address. 382 | * @return {Promise} A result and reason pair. 383 | */ 384 | async function canAdopt(contracts, escapee, address) { 385 | let res = { result: false }; 386 | // escapee must currently be trying to escape 387 | let point = await azimuth.getPoint(contracts, escapee, 'state'); 388 | if (!point.escapeRequested) { 389 | res.reason = reasons.notEscape; 390 | return res; 391 | } 392 | // caller must manage the requested sponsor 393 | return await checkActivePointManager(contracts, point.escapeRequestedTo, address); 394 | } 395 | 396 | /** 397 | * Check if the target address can reject the escapee's request to the given 398 | * sponsor. 399 | * @param {Object} contracts - An Urbit contracts object. 400 | * @param {Number} escapee - Escapee's point number. 401 | * @param {String} address - Target address. 402 | * @return {Promise} A result and reason pair. 403 | */ 404 | async function canReject(contracts, escapee, address) { 405 | // check is currently identical to adopt()'s. 406 | return await canAdopt(contracts, escapee, address); 407 | } 408 | 409 | /** 410 | * Check if the target address can detach a point from its sponsor. 411 | * @param {Object} contracts - An Urbit contracts object. 412 | * @param {Number} point - Point number. 413 | * @param {String} address - Target address. 414 | * @return {Promise} A result and reason pair. 415 | */ 416 | async function canDetach(contracts, point, address) 417 | { 418 | let res = { result: false }; 419 | // point must currently have a sponsor 420 | let thePoint = await azimuth.getPoint(contracts, point, 'state'); 421 | if (!thePoint.hasSponsor) { 422 | res.reason = reasons.sponsorless; 423 | return res; 424 | } 425 | // caller must manage the requested sponsor 426 | return await checkActivePointManager(contracts, thePoint.sponsor, address); 427 | } 428 | 429 | /** 430 | * Check if a point is active and an address can vote for it. 431 | * @param {Object} contracts - An Urbit contracts object. 432 | * @param {Number} point - Point number. 433 | * @param {String} voter - Target address. 434 | * @return {Promise} A result and reason pair. 435 | */ 436 | async function checkActivePointVoter(contracts, galaxy, voter) { 437 | let res = { result: false } 438 | if (azimuth.getPointSize(galaxy) !== azimuth.PointSize.Galaxy) 439 | { 440 | res.reason = reasons.notGalaxy; 441 | return res; 442 | } 443 | if (!await azimuth.canVoteAs(contracts, galaxy, voter)) 444 | { 445 | res.reason = reasons.permission; 446 | return res; 447 | } 448 | if (!await azimuth.isActive(contracts, galaxy)) 449 | { 450 | res.reason = reasons.inactive; 451 | return res; 452 | } 453 | res.result = true; 454 | return res; 455 | } 456 | 457 | /** 458 | * Check if a target address and point can initiate a upgrade poll at the 459 | * given address. 460 | * @param {Object} web3 - A web3 object. 461 | * @param {Object} contracts - An Urbit contracts object. 462 | * @param {Number} galaxy - A (galaxy) point number. 463 | * @param {String} proposal - The proposal address. 464 | * @param {String} address - Target address. 465 | * @return {Promise} A result and reason pair. 466 | */ 467 | async function canStartUpgradePoll(web3, contracts, galaxy, proposal, address) { 468 | let asv = await checkActivePointVoter(contracts, galaxy, address); 469 | if (!asv.result) return asv; 470 | let res = { result: false}; 471 | let prop = new web3.eth.Contract(eclipticAbi, proposal); 472 | let expected; 473 | try { 474 | expected = await prop.methods.previousEcliptic().call() 475 | } catch(e) { 476 | expected = null; 477 | } 478 | if (!expected || !utils.addressEquals(contracts.ecliptic.address, expected)) 479 | { 480 | res.reason = reasons.upgradePath; 481 | return res; 482 | } 483 | if (await polls.eclipticHasAchievedMajority(contracts, proposal)) 484 | { 485 | res.reason = reasons.majority; 486 | return res; 487 | } 488 | if (!canStartPoll(await polls.getUpgradePoll(contracts, proposal))) 489 | { 490 | res.reason = reasons.pollTime; 491 | return res; 492 | } 493 | res.result = true; 494 | return res; 495 | } 496 | 497 | /** 498 | * Check if a target address and point can initiate the given proposal. 499 | * @param {Object} contracts - An Urbit contracts object. 500 | * @param {Number} galaxy - A (galaxy) point number. 501 | * @param {String} proposal - The proposal address. 502 | * @param {String} address - Target address. 503 | * @return {Promise} A result and reason pair. 504 | */ 505 | async function canStartDocumentPoll(contracts, galaxy, proposal, address) { 506 | let asv = await checkActivePointVoter(contracts, galaxy, address); 507 | if (!asv.result) return asv; 508 | let res = { result: false }; 509 | if (await polls.documentHasAchievedMajority(contracts, proposal)) 510 | { 511 | res.reason = reasons.majority; 512 | return res; 513 | } 514 | if (!canStartPoll(await polls.getDocumentPoll(contracts, proposal))) 515 | { 516 | res.reason = reasons.pollTime; 517 | return res; 518 | } 519 | res.result = true; 520 | return res; 521 | } 522 | 523 | /** 524 | * Check if a target address and point can vote on the proposal at the target 525 | * address. 526 | * @param {Object} contracts - An Urbit contracts object. 527 | * @param {Number} galaxy - A (galaxy) point number. 528 | * @param {String} proposal - The proposal address. 529 | * @param {String} address - Target address. 530 | * @return {Promise} A result and reason pair. 531 | */ 532 | async function canCastUpgradeVote(contracts, galaxy, proposal, address) 533 | { 534 | let asv = await checkActivePointVoter(contracts, galaxy, address); 535 | if (!asv.result) return asv; 536 | let res = { result: false }; 537 | // proposal must not have achieved majority before 538 | if (await polls.eclipticHasAchievedMajority(contracts, proposal)) 539 | { 540 | res.reason = reasons.majority; 541 | return res; 542 | } 543 | // may only vote when the poll is open 544 | if (!pollIsActive(await polls.getUpgradePoll(contracts, proposal))) 545 | { 546 | res.reason = reasons.pollInactive; 547 | return res; 548 | } 549 | // may only vote once 550 | if (await polls.hasVotedOnUpgradePoll(contracts, galaxy, proposal)) 551 | { 552 | res.reason = reasons.pollVoted; 553 | return res; 554 | } 555 | res.result = true; 556 | return res; 557 | } 558 | 559 | /** 560 | * Check if a target address and point can vote on the proposal at the given 561 | * address. 562 | * @param {Object} contracts - An Urbit contracts object. 563 | * @param {Number} galaxy - A (galaxy) point number. 564 | * @param {String} proposal - The proposal address. 565 | * @param {String} address - Target address. 566 | * @return {Promise} A result and reason pair. 567 | */ 568 | async function canCastDocumentVote(contracts, galaxy, proposal, address) 569 | { 570 | let asv = await checkActivePointVoter(contracts, galaxy, address); 571 | if (!asv.result) return asv; 572 | let res = { result: false }; 573 | // proposal must not have achieved majority before 574 | if (await polls.documentHasAchievedMajority(contracts, proposal)) 575 | { 576 | res.reason = reasons.majority; 577 | return res; 578 | } 579 | // may only vote when the poll is open 580 | if (!pollIsActive(await polls.getDocumentPoll(contracts, proposal))) 581 | { 582 | res.reason = reasons.pollInactive; 583 | return res; 584 | } 585 | // may only vote once 586 | if (await polls.hasVotedOnDocumentPoll(contracts, galaxy, proposal)) 587 | { 588 | res.reason = reasons.pollVoted; 589 | return res; 590 | } 591 | res.result = true; 592 | return res; 593 | } 594 | 595 | /** 596 | * Check if the target address can set the DNS domains for the ecliptic. 597 | * @param {Object} contracts - An Urbit contracts object. 598 | * @param {String} address - Target address. 599 | * @return {Promise} True if so, false otherwise. 600 | */ 601 | function canSetDnsDomains(contracts, address) { 602 | return isEclipticOwner(contracts, address); 603 | } 604 | 605 | 606 | /** 607 | * Check if the address can withdraw a star from their batch at this moment. 608 | * @param {Object} contracts - An Urbit contracts object. 609 | * @param {String} address - The participant/registered address for the batch. 610 | * @return {Promise} A result and reason pair. 611 | */ 612 | async function lsrCanWithdraw(contracts, address) { 613 | let res = { result: false }; 614 | let rem = await linearSR.getRemainingStars(contracts, address); 615 | if (rem.length === 0) { 616 | res.reason = reasons.noRemaining; 617 | return res; 618 | } 619 | let bas = await linearSR.getBatch(contracts, address); 620 | let lim = await linearSR.getWithdrawLimit(contracts, address); 621 | if (bas.withdrawn >= lim) { 622 | res.reason = reasons.withdrawLimit; 623 | return res; 624 | } 625 | res.result = true; 626 | return res; 627 | } 628 | 629 | /** 630 | * Check if the address can withdraw a star from their batch at this moment. 631 | * @param {Object} contracts - An Urbit contracts object. 632 | * @param {String} from - The participant/registered address for the batch. 633 | * @param {String} to - The intended new address of the participant. 634 | * @return {Promise} A result and reason pair. 635 | */ 636 | async function lsrCanTransferBatch(contracts, from, to) { 637 | let res = { result: false }; 638 | let apr = await linearSR.getApprovedTransfer(contracts, from); 639 | if (to !== apr) { 640 | res.reason = reasons.notApproved; 641 | return res; 642 | } 643 | res.result = true; 644 | return res; 645 | } 646 | 647 | /** 648 | * Check if the address can withdraw a star from their commitment at this moment. 649 | * @param {Object} contracts - An Urbit contracts object. 650 | * @param {String} address - The participant/registered address for the commitment. 651 | * @return {Promise} A result and reason pair. 652 | */ 653 | async function csrCanWithdraw(contracts, address) { 654 | let res = { result: false }; 655 | let rem = await conditionalSR.getRemainingStars(contracts, address); 656 | if (rem.length === 0) { 657 | res.reason = reasons.noRemaining; 658 | return res; 659 | } 660 | let com = await conditionalSR.getCommitment(contracts, address); 661 | let lim = await conditionalSR.getWithdrawLimit(contracts, address); 662 | // cannot withdraw more than the limit 663 | if (com.withdrawn >= lim) { 664 | res.reason = reasons.withdrawLimit; 665 | return res; 666 | } 667 | // cannot withdraw forfeited stars 668 | if (com.forfeit && rem.length <= com.forfeited) { 669 | res.reason = reasons.forfeitLimit; 670 | return res; 671 | } 672 | res.result = true; 673 | return res; 674 | } 675 | 676 | /** 677 | * Check if the address can withdraw a star from their commitment at this moment. 678 | * @param {Object} contracts - An Urbit contracts object. 679 | * @param {String} from - The participant/registered address for the commitment. 680 | * @param {String} to - The intended new address of the participant. 681 | * @return {Promise} A result and reason pair. 682 | */ 683 | async function csrCanTransferBatch(contracts, from, to) { 684 | let res = { result: false }; 685 | let apr = await conditionalSR.getApprovedTransfer(contracts, from); 686 | if (to !== apr) { 687 | res.reason = reasons.notApproved; 688 | return res; 689 | } 690 | res.result = true; 691 | return res; 692 | } 693 | 694 | /** 695 | * Check if the address can forfeit their commitment starting at the batch. 696 | * @param {Object} contracts - An Urbit contracts object. 697 | * @param {Number} batch - The batch they want to forfeit from. 698 | * @param {String} address - The participant/registered address for the 699 | * commitment. 700 | * @return {Promise} A result and reason pair. 701 | */ 702 | async function csrCanForfeit(contracts, batch, address) { 703 | let res = { result: false }; 704 | let com = await conditionalSR.getCommitment(contracts, address); 705 | // can only forfeit if not yet forfeited, 706 | if (com.forfeit) { 707 | res.reason = reasons.hasForfeited; 708 | return res; 709 | } 710 | let det = await conditionalSR.getConditionsState(contracts); 711 | // and deadline has been missed. 712 | if (det.deadlines[batch] !== det.timestamps[batch]) { 713 | res.reason = reasons.notMissed; 714 | return res; 715 | } 716 | res.result = true; 717 | return res; 718 | } 719 | 720 | /** 721 | * Check if the address can give invites to point 722 | * @param {Number} point - Point number to give invites to 723 | * @param {String} address - The caller address 724 | * @return {Promise} A result and reason pair 725 | */ 726 | async function canSetPoolSize(contracts, point, address) { 727 | return checkActivePointOwner(contracts, azimuth.getPrefix(point), address); 728 | } 729 | 730 | /** 731 | * Check if as can send point as an invite to to 732 | * @param {Number} as - The point that would send the invite 733 | * @param {Number} point - The point that would be sent as invite 734 | * @param {String} to - The Ethereum address recipient of the invite 735 | * @param {String} address - The caller address 736 | * @return {Promise} A result and reason pair 737 | */ 738 | async function canSendInvitePoint(contracts, as, point, to, address) { 739 | let res = await checkActivePointOwner(contracts, as, address); 740 | if (!res.result) { 741 | return res; 742 | } 743 | res.result = false; 744 | if (utils.addressEquals(to, address)) { 745 | res.reason = reasons.cantReceive; 746 | return res; 747 | } 748 | let canSend = await delegatedSending.canSend(contracts, as, point); 749 | if (!canSend) { 750 | res.reason = reasons.permission; 751 | return res; 752 | } 753 | let canReceive = await delegatedSending.canReceive(contracts, to); 754 | if (!canReceive) { 755 | res.reason = reasons.cantReceive; 756 | return res; 757 | } 758 | res.result = true; 759 | return res; 760 | } 761 | 762 | 763 | module.exports = { 764 | ecliptic, 765 | azimuth, 766 | polls, 767 | isPoint, 768 | isGalaxy, 769 | isStar, 770 | isPlanet, 771 | isPrefix, 772 | isChild, 773 | pollIsActive, 774 | canStartPoll, 775 | hasOwner, 776 | isEclipticOwner, 777 | canCreateGalaxy, 778 | canSpawn, 779 | canSetManagementProxy, 780 | canSetVotingProxy, 781 | canSetSpawnProxy, 782 | canTransferPoint, 783 | canSetTransferProxy, 784 | canConfigureKeys, 785 | canEscape, 786 | canAdopt, 787 | canReject, 788 | canDetach, 789 | checkActivePointManager, 790 | checkActivePointVoter, 791 | canStartUpgradePoll, 792 | canStartDocumentPoll, 793 | canCastUpgradeVote, 794 | canCastDocumentVote, 795 | canSetDnsDomains, 796 | lsrCanWithdraw, 797 | lsrCanTransferBatch, 798 | csrCanWithdraw, 799 | csrCanTransferBatch, 800 | csrCanForfeit, 801 | canSetPoolSize, 802 | canSendInvitePoint, 803 | } 804 | --------------------------------------------------------------------------------