Skip to content

Commit

Permalink
Fabo+Mario/transaction abstraction (#336)
Browse files Browse the repository at this point in the history
* draft for transaction abstraction

* use lunie message types

* Add supported tx types

* WIP

* Draft new reducers

* Fix msg types

* Need to resolve type for TransactionDetails union

* debug resolver

* lint

* Fix

* Cleanup

* fix some union issues

* Almost done

* Small fixes

* Add userTransactionAddedV2

* Add userTransactionAddedV2 to resolvers

Co-authored-by: Mario Pino <info@quequiereshacer.es>
  • Loading branch information
faboweb and mariopino authored Feb 19, 2020
1 parent fdd5d0b commit 3f4195f
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 7 deletions.
18 changes: 17 additions & 1 deletion lib/message-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ const cosmosWhitelistedMessageTypes = new Set([
`MsgMultiSend`
])

module.exports = { cosmosMessageType, cosmosWhitelistedMessageTypes }
const lunieMessageTypes = {
SEND: `SendTx`,
STAKE: `StakeTx`,
RESTAKE: `RestakeTx`,
UNSTAKE: `UnstakeTx`,
VOTE: `VoteTx`,
DEPOSIT: `DepositTx`,
WITHDRAW_DELEGATION_REWARDS: `ClaimRewardsTx`,
SUBMIT_PROPOSAL: `SubmitProposalTx`,
UNKNOWN: `UnknownTx`
}

module.exports = {
cosmosMessageType,
cosmosWhitelistedMessageTypes,
lunieMessageTypes
}
165 changes: 164 additions & 1 deletion lib/reducers/cosmosV0-reducers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const { uniqWith, sortBy, reverse } = require('lodash')
const { cosmosMessageType } = require('../message-types')
const { cosmosWhitelistedMessageTypes } = require('../../lib/message-types')
const {
cosmosWhitelistedMessageTypes,
lunieMessageTypes
} = require('../../lib/message-types')
const BigNumber = require('bignumber.js')
const _ = require('lodash')
const Sentry = require('@sentry/node')
Expand Down Expand Up @@ -406,6 +409,41 @@ function formatTransactionsReducer(txs, reducers) {
return filteredTxs.map(tx => transactionReducer(tx, reducers))
}

function transactionReducerV2(transaction, reducers) {
// TODO check if this is anywhere not an array
let fees
if (Array.isArray(transaction.tx.value.fee.amount)) {
fees = transaction.tx.value.fee.amount.map(coinReducer)
} else {
fees = [coinReducer(transaction.tx.value.fee.amount)]
}
// We do display only the transactions we support in Lunie
const filteredMessages = transaction.tx.value.msg.filter(
({ type }) => getMessageType(type) !== 'Unknown'
)
const returnedMessages = filteredMessages.map(({ value, type }, index) => ({
type: getMessageType(type),
hash: transaction.txhash,
height: transaction.height,
details: transactionDetailsReducer(getMessageType(type), value, reducers),
timestamp: transaction.timestamp,
memo: transaction.tx.value.memo,
fees,
success: transaction.logs ? transaction.logs[index].success : false
}))
return returnedMessages
}

function transactionsReducerV2(txs, reducers) {
const duplicateFreeTxs = uniqWith(txs, (a, b) => a.txhash === b.txhash)
const sortedTxs = sortBy(duplicateFreeTxs, ['timestamp'])
const reversedTxs = reverse(sortedTxs)
// here we filter out all transactions related to validators
return reversedTxs.reduce((collection, transaction) => {
return collection.concat(transactionReducerV2(transaction, reducers))
}, [])
}

// to be able to catch all validators from a multi-claim reward tx, we need to capture
// more than just the first value message.
function txMultiClaimRewardReducer(txMessages) {
Expand Down Expand Up @@ -459,6 +497,130 @@ function transactionReducer(transaction, reducers) {
}
}

// map Cosmos SDK message types to Lunie message types
function getMessageType(type) {
// different networks use different prefixes for the transaction types like cosmos/MsgSend vs core/MsgSend in Terra
const transactionTypeSuffix = type.split('/')[1]
switch (transactionTypeSuffix) {
case 'MsgSend':
return lunieMessageTypes.SEND
case 'MsgDelegate':
return lunieMessageTypes.STAKE
case 'MsgBeginRedelegate':
return lunieMessageTypes.RESTAKE
case 'MsgUndelegate':
return lunieMessageTypes.UNSTAKE
case 'MsgWithdrawDelegationReward':
return lunieMessageTypes.WITHDRAW_DELEGATION_REWARDS
case 'MsgSubmitProposal':
return lunieMessageTypes.SUBMIT_PROPOSAL
case 'MsgVote':
return lunieMessageTypes.VOTE
case 'MsgDeposit':
return lunieMessageTypes.DEPOSIT
default:
return lunieMessageTypes.UNKNOWN
}
}

// function to map cosmos messages to our details format
function transactionDetailsReducer(type, message, reducers) {
let details
switch (type) {
case lunieMessageTypes.SEND:
details = sendDetailsReducer(message, reducers)
break
case lunieMessageTypes.STAKE:
details = stakeDetailsReducer(message, reducers)
break
case lunieMessageTypes.RESTAKE:
details = restakeDetailsReducer(message, reducers)
break
case lunieMessageTypes.UNSTAKE:
details = unstakeDetailsReducer(message, reducers)
break
case lunieMessageTypes.WITHDRAW_DELEGATION_REWARDS:
details = claimRewardsDetailsReducer(message, reducers)
break
case lunieMessageTypes.SUBMIT_PROPOSAL:
details = submitProposalDetailsReducer(message, reducers)
break
case lunieMessageTypes.VOTE:
details = voteProposalDetailsReducer(message, reducers)
break
case lunieMessageTypes.DEPOSIT:
details = depositDetailsReducer(message, reducers)
break
default:
details = {}
}

return {
type,
...details
}
}

function sendDetailsReducer(message, reducers) {
return {
to: [message.to_address],
amount: reducers.coinReducer(message.amount[0])
}
}

function stakeDetailsReducer(message, reducers) {
return {
to: [message.validator_address],
amount: reducers.coinReducer(message.amount)
}
}

function restakeDetailsReducer(message, reducers) {
return {
from: [message.validator_src_address],
to: [message.validator_dst_address],
amount: reducers.coinReducer(message.amount)
}
}

function unstakeDetailsReducer(message, reducers) {
return {
from: [message.validator_address],
amount: reducers.coinReducer(message.amount)
}
}

function claimRewardsDetailsReducer(message, reducers) {
return {
from: [message.validator_address],
amount: reducers.coinReducer(message.amount)
}
}

function submitProposalDetailsReducer(message, reducers) {
return {
proposalType: message.content.type,
proposalTitle: message.content.value.title,
proposalDescription: message.content.value.description,
initialDeposit: reducers.coinReducer(message.initial_deposit[0])
}
}

function voteProposalDetailsReducer(message) {
return {
proposalId: message.proposal_id,
voteOption: message.option
}
}

// TO TEST!
function depositDetailsReducer(message, reducers) {
return {
proposalId: message.proposal_id,
amount: reducers.coinReducer(message.amount)
}
}

module.exports = {
proposalReducer,
governanceParameterReducer,
Expand All @@ -476,6 +638,7 @@ module.exports = {
calculateTokens,
undelegationEndTimeReducer,
formatTransactionsReducer,
transactionsReducerV2,

atoms,
proposalBeginTime,
Expand Down
14 changes: 13 additions & 1 deletion lib/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ const resolvers = {
return overview
},
transactions: (_, { networkId, address }, { dataSources }) =>
remoteFetch(dataSources, networkId).getTransactions(address)
remoteFetch(dataSources, networkId).getTransactions(address),
transactionsV2: (_, { networkId, address }, { dataSources }) =>
remoteFetch(dataSources, networkId).getTransactionsV2(address)
},
Subscription: {
blockAdded: {
Expand All @@ -326,6 +328,10 @@ const resolvers = {
subscribe: (_, { networkId, address }) =>
userTransactionAdded(networkId, address)
},
userTransactionAddedV2: {
subscribe: (_, { networkId, address }) =>
userTransactionAdded(networkId, address)
},
event: {
subscribe: withFilter(
event,
Expand All @@ -340,6 +346,12 @@ const resolvers = {
}
)
}
},
// Resolve type for TransactionDetails union
TransactionDetails: {
__resolveType(obj) {
return obj.type
}
}
}

Expand Down
26 changes: 22 additions & 4 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,28 +160,39 @@ const typeDefs = gql`
SendTx
| StakeTx
| UnstakeTx
| RestakeTx
| ClaimRewardsTx
| VoteProposalTx
| SubmitProposalTx
| VoteTx
| DepositTx
type TransactionV2 {
type: String!
hash: String!
height: Int!
details: TransactionDetails
timestamp: String!
memo: String
fees: [Coin]!
success: Boolean!
}
type SendTx {
amount: Coin!
to: String!
memo: String
to: [String]!
}
type StakeTx {
amount: Coin!
to: [String]!
}
type RestakeTx {
amount: Coin!
from: [String]!
to: [String]!
}
type UnstakeTx {
amount: Coin!
from: [String]!
Expand All @@ -199,11 +210,16 @@ const typeDefs = gql`
initialDeposit: Coin!
}
type VoteProposalTx {
type VoteTx {
proposalId: Int!
voteOption: String!
}
type DepositTx {
proposalId: Int!
amount: Coin!
}
type GovernanceParameters {
depositDenom: String
votingThreshold: Float
Expand Down Expand Up @@ -247,6 +263,7 @@ const typeDefs = gql`
type Subscription {
blockAdded(networkId: String!): Block
userTransactionAdded(networkId: String!, address: String!): Transaction
userTransactionAddedV2(networkId: String!, address: String!): TransactionV2
event(networkId: String!, eventType: String!, ressourceId: String): Event
}
Expand Down Expand Up @@ -292,6 +309,7 @@ const typeDefs = gql`
operatorAddress: String
): [Reward]
transactions(networkId: String!, address: String!): [Transaction]
transactionsV2(networkId: String!, address: String!): [TransactionV2]
}
`

Expand Down
11 changes: 11 additions & 0 deletions lib/source/cosmosV2-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ class CosmosV2API extends CosmosV0API {
return this.reducers.formatTransactionsReducer(txs, this.reducers)
}

async getTransactionsV2(address) {
this.checkAddress(address)

const txs = await Promise.all([
this.loadPaginatedTxs(`/txs?message.sender=${address}`),
this.loadPaginatedTxs(`/txs?transfer.recipient=${address}`)
]).then(([senderTxs, recipientTxs]) => [].concat(senderTxs, recipientTxs))

return this.reducers.transactionsReducerV2(txs, this.reducers)
}

extractInvolvedAddresses(transaction) {
// If the transaction has failed, it doesn't get tagged
if (!Array.isArray(transaction.events)) return []
Expand Down

0 comments on commit 3f4195f

Please sign in to comment.