Skip to content
This repository has been archived by the owner on Mar 16, 2023. It is now read-only.

Commit

Permalink
Merge pull request #505 from AztecProtocol/feat/transaction-history
Browse files Browse the repository at this point in the history
Feat/transaction history
  • Loading branch information
Joe Andrews authored Mar 24, 2020
2 parents 13f1969 + c8d59dc commit 9c808be
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 2 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@
"packages/*"
],
"dependencies": {
"@semantic-release/exec": "^4.0.0"
"@semantic-release/exec": "^4.0.0",
"eslint-plugin-react": "^7.19.0",
"global": "^4.4.0"
}
}
2 changes: 1 addition & 1 deletion packages/documentation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"eslint-import-resolver-babel-module": "^5.0.1",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react": "^7.19.0",
"jest": "^24.9.0",
"postcss-cssnext": "^3.1.0",
"source-map-loader": "^0.2.4",
Expand Down
5 changes: 5 additions & 0 deletions packages/documentation/styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,11 @@ module.exports = {
content: 'styleguide/categories/WebSDK/ZkAsset/utility/subscribeToBalance.md',
exampleMode: 'hide',
},
{
name: '.transactions',
content: 'styleguide/categories/WebSDK/ZkAsset/utility/transactions.md',
exampleMode: 'hide',
},
{
name: '.createNoteFromBalance',
content: 'styleguide/categories/WebSDK/ZkAsset/utility/createNoteFromBalance.md',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Main APIs:
Utilities:

- [`async balance()`](/#/SDK/zkAsset/.balance)
- [`async transactions()`](/#/SDK/zkAsset/.transactions)
- [`async subscribeToBalance()`](/#/SDK/zkAsset/.subscribeToBalance)
- [`async createNotesFromBalance()`](/#/SDK/zkAsset/.createNotesFromBalance)
- [`async fetchNotesFromBalance()`](/#/SDK/zkAsset/.fetchNotesFromBalance)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The SDK ships with a set of useful utilities that make it easier to handle AZTEC's UTXO note model and convert between note and token values.

- `async balance()`
- `async transactions()`
- `async subscribeToBalance()`
- `async createNoteFromBalance()`
- `async fetchNotesFromBalance()`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Examples

### Fetch the users transaction history for a particular asset

```js
const zkAssetAddress = '';
const asset = await window.aztec.zkAsset(zkAssetAddress);

// Fetch txs
const txs = await asset.transactions();
console.info({ txs });
```
1 change: 1 addition & 0 deletions packages/documentation/styleguide/config/apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const zkAssetApis = [
'.send',
'.withdraw',
'.balance',
'.transactions',
'.subscribeToBalance',
'.createNoteFromBalance',
'.fetchNotesFromBalance',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import noteQuery from '~/background/services/GraphQLService/Queries/noteQuery';
import Web3Service from '~/helpers/Web3Service';
import {
ZkAsset,
} from '~/config/contractEvents';
import asyncMap from '~/utils/asyncMap';
import query from '../utils/query';

export default async function fetchTransactions(request) {
const {
data: {
args: {
assetAddress,
type,
fromBlock,
toBlock,
},
},
} = request;

const {
address: currentAddress,
} = Web3Service.account;

const options = {
fromBlock,
toBlock,
};

options.filter = {
owner: currentAddress,
};

const createEvents = await Web3Service
.useContract('ZkAsset')
.at(assetAddress)
.events(ZkAsset.createNote)
.where(options);
const destroyEvents = await Web3Service
.useContract('ZkAsset')
.at(assetAddress)
.events(ZkAsset.destroyNote)
.where(options);

const redeemEvents = await Web3Service
.useContract('ZkAsset')
.at(assetAddress)
.events('RedeemTokens')
.where(options);
const events = [].concat(destroyEvents, createEvents, redeemEvents);
const eventsByTxhash = {};
events.forEach(({
transactionHash,
event,
...rest
}) => {
if (!eventsByTxhash[transactionHash]) {
eventsByTxhash[transactionHash] = {
CreateNote: [],
DestroyNote: [],
RedeemTokens: [],
blockNumber: rest.blockNumber,
};
}
eventsByTxhash[transactionHash][event].push({
event,
...rest,
});
});
const transactions = await asyncMap(Object.keys(eventsByTxhash), async (txHash) => {
let txType;
const txEvents = eventsByTxhash[txHash];


const txTimestamp = (await Web3Service.web3.eth.getBlock(txEvents.blockNumber)).timestamp;
// async map every note to get its value
const createValues = await asyncMap(txEvents.CreateNote,
({
returnValues: { noteHash },
}) => query({
...request,
data: {
args: {
id: noteHash,
},
},
},
noteQuery(`
value,
owner {
address
}
`)));
const destroyValues = await asyncMap(txEvents.DestroyNote, ({
returnValues: { noteHash },
}) => query({
...request,
data: {
args: {
id: noteHash,
},
},
}, noteQuery(`
value
`)));
const outgoing = destroyValues
.reduce((accum, {
note: {
note: {
value,
},
},
}) => value + accum, 0);

const incoming = createValues
.reduce((accum, {
note: {
note: {
value,
},
},
}) => value + accum, 0);


let value = -outgoing + incoming;
if (txEvents.CreateNote.length && !txEvents.DestroyNote.length) {
// this is easy its a deposit
txType = 'DEPOSIT';
}
if (txEvents.DestroyNote.length && !txEvents.CreateNote.length) {
// this is easy its a withdraw
txType = 'WITHDRAW';
}


if (txEvents.DestroyNote.length && txEvents.CreateNote.length) {
// this is hard its either a send or a withdraw
const allOwner = txEvents
.CreateNote.every(event => event.returnValues.owner === currentAddress);
txType = allOwner && value ? 'WITHDRAW' : 'SEND';
}

if (txEvents.DestroyNote.length
&& txEvents.CreateNote.length
&& value === 0 && txType === 'SEND') {
value = outgoing;
}

let to = [...new Set(createValues.map(({
note: {
note: {
owner: {
address,
},
},
},
}) => address))];

if (!to.length) {
to = [txEvents.RedeemTokens[0].returnValues.owner];
}
return {
txHash,
type: txType,
noteValue: value,
to,
timestamp: txTimestamp,
};
});
transactions.sort((a, b) => {
if (a.timestamp > b.timestamp) return -1;
if (a.timestamp < b.timestamp) return 1;
return 0;
});

return { data: type ? transactions.filter(tx => tx.type === type) : transactions };
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import prove from './Prove';
import asset from './Asset';
import assetBalance from './Asset/getBalance';
import fetchNotesFromBalance from './Asset/fetchNotesFromBalance';
import fetchTransactions from './Asset/fetchTransactions';
import note from './Note';
import noteWithViewingKey from './Note/exportViewingKey';
import grantNoteAccess from './Note/grantNoteAccess';
Expand All @@ -26,6 +27,7 @@ const apis = {
asset,
assetBalance,
fetchNotesFromBalance,
fetchTransactions,
note,
noteWithViewingKey,
grantNoteAccess,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import makeSchema from '~/utils/makeSchema';
import addressType from './types/address';

export default makeSchema({
type: {
type: 'string',
},
assetAddress: addressType.isRequired,
fromBlock: {
type: 'number',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import users from './users';
import encryptMessage from './encryptMessage';
import decryptMessage from './decryptMessage';
import asset from './asset';
import fetchTransactions from './fetchTransactions';
import assetBalance from './assetBalance';
import fetchNotesFromBalance from './fetchNotesFromBalance';
import note from './note';
Expand All @@ -28,6 +29,7 @@ export default {
note,
noteWithViewingKey,
grantNoteAccess,
fetchTransactions,
constructProof: {
DEPOSIT_PROOF,
WITHDRAW_PROOF,
Expand Down
39 changes: 39 additions & 0 deletions packages/extension/src/client/apis/ZkAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,45 @@ export default class ZkAsset {
return notes;
};

/**
*
* @function zkAsset.transactions
* @description Description: Fetch the users transactions for the `zkAsset` .
*
* @param {Object} query Optional query object that can be used to refine the parameters of the note fetch.
* If not supplied, will return all the notes owned by the user.
*
* - *type* (String): The transaction type one of SEND, DEPOSIT, WITHDAW.
* - *toBlock* (Integer): The transaction type one of SEND, DEPOSIT, WITHDAW.
* - *fromBlock* (Integer): The transaction type one of SEND, DEPOSIT, WITHDAW.
*
* @returns {[Tx]} transactions an array of transactions that satisfy the parameters of the fetch query. Each tx is an object containing:
*
*
* - *to* ([String]) : the address the transaction was sent to
*
* - *timestamp* (Integer)
*
* - *value* (Integer): the total value transfered
*
* - *type* (String): on of SEND, DEPOSIT, WITHDRAW
*
*/

transactions = async ({
type,
fromBlock,
toBlock,
} = {}) => ConnectionService.query(
'fetchTransactions',
{
assetAddress: this.address,
type,
fromBlock,
toBlock,
},
);

/**
*
* @function zkAsset.fetchNotesFromBalance
Expand Down

0 comments on commit 9c808be

Please sign in to comment.