UNPKG

machinomy

Version:

Micropayments powered by Ethereum

143 lines 6.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const bignumber_js_1 = require("bignumber.js"); const logger_1 = require("@machinomy/logger"); const contracts = require("@machinomy/contracts"); const ChannelId_1 = require("./ChannelId"); const MemoryCache_1 = require("./caching/MemoryCache"); const abi = require("ethereumjs-abi"); const sigUtil = require("eth-sig-util"); const ChannelState_1 = require("./ChannelState"); const ChannelManager_1 = require("./ChannelManager"); const log = new logger_1.default('channel-token-contract'); const CREATE_CHANNEL_GAS = 300000; class ChannelTokenContract { constructor(web3, ttl) { this.web3 = web3; this.contract = contracts.TokenUnidirectional.contract(this.web3.currentProvider).deployed(); this.cache = new MemoryCache_1.default(ttl); } async open(sender, receiver, value, settlementPeriod, tokenContract, channelId) { log.info(`Creating channel. Value: ${value} / Settlement: ${settlementPeriod}`); let _channelId = channelId || ChannelId_1.default.random(); const standardTokenContract = contracts.StandardToken.contract(this.web3.currentProvider).at(tokenContract); const deployedTokenUnidirectional = await this.contract; const deployedStandardTokenContract = await standardTokenContract; const approveTx = await deployedStandardTokenContract.approve(deployedTokenUnidirectional.address, value, { from: sender }); if (contracts.StandardToken.isApprovalEvent(approveTx.logs[0])) { return deployedTokenUnidirectional.open(_channelId.toString(), receiver, new bignumber_js_1.BigNumber(settlementPeriod), tokenContract, value, { from: sender, gas: CREATE_CHANNEL_GAS }); } else { const errorMessage = `Opening channel. Can not approve tokens hold from sender ${sender} to receiver ${receiver}. Value: ${value}`; log.error(errorMessage); return Promise.reject(errorMessage); } } async claim(receiver, channelId, value, signature) { log.info(`Claiming channel with id ${channelId} on behalf of receiver ${receiver}`); log.info(`Values: ${value} / Signature: ${signature.toString()}`); const deployed = await this.contract; return deployed.claim(channelId, value, signature.toString(), { from: receiver, gas: CREATE_CHANNEL_GAS }); } async deposit(sender, channelId, value, tokenContract) { log.info(`Depositing ${value} into channel ${channelId}`); const standardTokenContract = contracts.StandardToken.contract(this.web3.currentProvider).at(tokenContract); const deployedTokenUnidirectional = await this.contract; const deployedStandardTokenContract = await standardTokenContract; const channel = await this.channelById(channelId); if (!channel) throw new Error('Can not deposit to channel not present'); const receiver = channel[1]; const approveTx = await deployedStandardTokenContract.approve(receiver, value, { from: sender }); if (contracts.StandardToken.isApprovalEvent(approveTx.logs[0])) { return deployedTokenUnidirectional.deposit(channelId, value, { from: sender, gas: CREATE_CHANNEL_GAS }); } else { const errorMessage = `Deposit operation. Can not approve tokens hold from sender ${sender} to receiver ${receiver}. Value: ${value}`; log.error(errorMessage); return Promise.reject(errorMessage); } } async getState(channelId) { log.info(`Fetching state for channel ${channelId}`); const channel = await this.channelById(channelId); if (channel) { const settlingPeriod = channel[3]; const settlingUntil = channel[4]; log.info(`Fetched state for channel ${channelId}`); if (settlingPeriod.gt(0) && settlingUntil.gt(0)) { return ChannelState_1.ChannelState.Settling; } else if (settlingPeriod.gt(0) && settlingUntil.eq(0)) { return ChannelState_1.ChannelState.Open; } else { return ChannelState_1.ChannelState.Settled; } } else { return ChannelState_1.ChannelState.Settled; } } async getSettlementPeriod(channelId) { log.info(`Fetching settlement period for channel ${channelId}`); const channel = await this.channelById(channelId); if (channel) { return channel[3]; } else { return new bignumber_js_1.BigNumber(ChannelManager_1.default.DEFAULT_SETTLEMENT_PERIOD); } } async startSettle(account, channelId) { log.info(`Starting settle for account ${account} and channel id ${channelId}.`); const deployed = await this.contract; return deployed.startSettling(channelId, { from: account }); } async finishSettle(account, channelId) { log.info(`Finishing settle for account ${account} and channel ID ${channelId}.`); const deployed = await this.contract; return deployed.settle(channelId, { from: account, gas: 400000 }); } async paymentDigest(channelId, value, tokenContract) { const deployed = await this.contract; const digest = abi.soliditySHA3(['address', 'bytes32', 'uint256', 'address'], [deployed.address, channelId, value.toString(), tokenContract]); return '0x' + digest.toString('hex'); } async canClaim(channelId, payment, receiver, signature) { const channel = await this.channelById(channelId); if (!channel) return false; const sender = channel[0]; const tokenAddress = channel[5]; let digest = await this.paymentDigest(channelId, payment, tokenAddress); let recovered = sigUtil.recoverPersonalSignature({ data: digest, sig: signature.toString() }); return recovered === sender; } async channelById(channelId) { const cached = await this.cache.get(channelId); if (cached) { return cached; } else { const deployed = await this.contract; const exists = await deployed.isPresent(channelId); if (!exists) return undefined; const instance = await deployed.channels(channelId); await this.cache.set(channelId, instance); return instance; } } } exports.default = ChannelTokenContract; //# sourceMappingURL=ChannelTokenContract.js.map