Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 216 additions & 98 deletions packages/contracts/contracts/RToken.sol

Large diffs are not rendered by default.

370 changes: 243 additions & 127 deletions packages/contracts/test/subgraphDeployment.test.js

Large diffs are not rendered by default.

75 changes: 45 additions & 30 deletions packages/subgraph/README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
# rToken Subgraph

> :warning: Warning: While the deployed version is stable, the code in this package is under active development. Please contact the team if you have questions via twitter/discord.
> Data provider for rDAI / rToken dapps

## What you can do:

#### Use it now

Subgraph for rDAI on mainnet is provided by TheGraph, and can be found [at this link](https://thegraph.com/explorer/subgraph/pi0neerpat/mcd-rdai).
:warning: Warning: While the deployed version is stable, the code in this package may be under development. Please contact the team if you have questions via twitter/discord.

#### Develop it
## What you can do:

See [Local development](#local-development).
- **Use the Official rDAI subgraph** provided by TheGraph - see [link](https://thegraph.com/explorer/subgraph/rtoken-project/rdai).

#### Use it with your own rToken
- **Develop and improve the subgraph** - see [Local development](#local-development).

See [Bring-your-own rToken](#bring-your-own-rtoken).
- **Implement your own rToken subgraph** - see [Bring-your-own rToken](#bring-your-own-rtoken).

## Local development

> :warning: You probably don't need to do this! If your rToken is deployed to `Mainnet` or `Ropsten`, and you are using the standard rToken contracts, then you should just use the hosted version provided by The Graph.
> Hold up :exclamation: You probably don't need this section. If you are using rToken on `Mainnet` or `Ropsten` you should just use an existing subgraph from The Graph's website.

The rToken team uses a local subgraph deployment to enable rapid development and testing of the tools provided here. In this section we will do the following:
The rToken team uses this local subgraph deployment flow to enable rapid development and testing of the tools provided here. In this section we will do the following:

1. Deploy the subgraph to a `docker container`.
2. Deploy the rToken contracts to `Ganache`.
Expand All @@ -41,56 +37,74 @@ Install the necessary packages:
yarn global add truffle ganache-cli @graphprotocol/graph-cli
```

Start ganache. It's helpful to specify a mnemonic, so you can hard-code the address in `subgraph.yaml` and the test files.

```bash
ganache-cli -h 0.0.0.0 -m 'deputy taste judge cave mosquito supply hospital clarify argue aware abuse glory'
```

Download the `graph-node` Docker instance.

```bash
git clone https://github.com/graphprotocol/graph-node/
cd graph-node/docker
```

Then navigate to `graph-node/docker`

If on Linux, run the following script.

> Note I had problems here, so you may need to troubleshoot by first running `docker-compose create` or `docker-compose up`. If you get a "version" error, update your docker-compose with [these instructions](https://docs.docker.com/compose/install/). If you get an error like `ERROR: could not find an available, non-overlapping IPv4 address...` then try turning off OpenVPN, or follow [this tutorial](https://stackoverflow.com/questions/45692255/how-make-openvpn-work-with-docker).
> Note I had problems here. If you get a "version" error, update your docker-compose with [these instructions](https://docs.docker.com/compose/install/). If you get an error like `ERROR: could not find an available, non-overlapping IPv4 address...` then try turning off OpenVPN, or follow [this tutorial](https://stackoverflow.com/questions/45692255/how-make-openvpn-work-with-docker).

```bash
sudo apt install jq # if necessary
./setup.sh
# For Linux machines
sudo apt install jq
./setup.sh # writes the host IP to the docker-compose file
```

Now lets start our subgraph Docker instance.
Now start the necessary subgraph Docker containers.

```bash
docker-compose up
# leave running
# or to run in background
docker-compose up -d

# Check its running
docker logs docker_graph-node_1
```

#### Deploy the contracts to Ganache

In a new terminal, navigate to `@rtoken/contracts` and start running ganache-cli and deploy the contracts.
Now in `rtoken-monorepo` navigate to `packages/contracts`, deploy the contracts, and copy the address for `rTOKEN contract (proxy)`.

```bash
ganache-cli -h 0.0.0.0 -m 'deputy taste judge cave mosquito supply hospital clarify argue aware abuse glory'
# using the same mnemonic allows for hard-coding the address in `subgraph.yaml` and the test files. It's not the best way, but it works!

# Then in a new terminal
# Install dependencies
lerna bootstrap
# Deploy contracts
truffle test --network subgraph test/subgraphDeployment.test.js
# Copy the rToken contract address
> ...
> The rTOKEN contract (proxy) is deployed at: 0x625aD1AA6469568ded0cd0254793Efd0e5C0394F
```

Copy the deployed rToken contract address printed at the start of the deployment process. If you used the same mnemonic, then this step isn't necessary
We also need to build the contracts, for use in the `subgraph` package.

```
> truffle test --network subgraph test/subgraphDeployment.test.js
...
The rTOKEN contract (proxy) is deployed at: 0xc97EeFc57dD8E74A30AC2cC52E8785B40a14a30c
```bash
# Build and save the contracts
yarn build
```

#### Deploy the Subgraph

Navigate back to this package and paste the contract address in `subgraph.yaml`. We are now ready to deploy our subgraph.
Now in `rtoken-monorepo` navigate to `packages/subgraph` and paste the contract address in `subgraph.yaml`. We are ready to deploy our subgraph.

```bash
# Copy the abis from packages/contracts and generate the subgraph schema
yarn codegen
yarn create-local # Only run once

# Create the subgraph node "rtoken-test" (only run once)
yarn create-local

# Deploy the subgraph to "rtoken-test" node
yarn deploy-local
```

Expand Down Expand Up @@ -156,6 +170,7 @@ sudo rm -rf data && docker-compose up
3. Re-deploy the new subgraph, whenever subgraph.yaml is changed:

```bash
yarn create-local
yarn deploy-local --watch
```

Expand Down
25 changes: 16 additions & 9 deletions packages/subgraph/getAbi.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
const fs = require('fs')
const RToken = require('@rtoken/contracts/build/contracts/RToken')
const fs = require("fs");
const RToken = require("@rtoken/contracts/build/contracts/RToken");
const ias = require("@rtoken/contracts/build/contracts/IAllocationStrategy");

fs.mkdir('abis/', err => {
fs.mkdir("abis/", err => {
if (err) {
// console.error(err)
return
return;
}
console.log("abis/ directory created");
})
fs.writeFile('abis/RToken.json', JSON.stringify(RToken), err => {
});
fs.writeFile("abis/RToken.json", JSON.stringify(RToken), err => {
if (err) {
console.error(err)
return
console.error(err);
return;
}
});
fs.writeFile("abis/IAllocationStrategy.json", JSON.stringify(ias), err => {
if (err) {
console.error(err);
return;
}
console.log("abi files updated");
})
});
2 changes: 1 addition & 1 deletion packages/subgraph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"create-local": "graph create --node http://localhost:8020/ rtoken-test",
"remove-local": "graph remove --node http://localhost:8020/ rtoken-test",
"deploy-local": "graph deploy --node http://localhost:8020/ --ipfs http://localhost:5001 rtoken-test",
"deploy": "graph deploy --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs/rtoken-project/rtoken-analytics"
"deploy": "graph deploy --node https://api.thegraph.com/deploy/ --ipfs https://api.thegraph.com/ipfs rtoken-project/rdai"
},
"dependencies": {
"@graphprotocol/graph-cli": "0.16.1",
Expand Down
5 changes: 4 additions & 1 deletion packages/subgraph/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Account @entity {
balance: BigDecimal! # in rDai (rAmount)
# ---------------------------------- links ----------------------------------
loansOwned: [Loan!]! @derivedFrom(field: "owner")
loansReceived: [Loan!]! @derivedFrom(field: "recipient")
loansReceived: [Loan!]!
hatMember: [HatMembership!]! @derivedFrom(field: "account")
transfersFrom: [Transfer!]! @derivedFrom(field: "from")
transfersTo: [Transfer!]! @derivedFrom(field: "to")
Expand All @@ -31,6 +31,9 @@ type Loan @entity {
recipient: Account!
amount: BigDecimal! # in rDai (accounts[owner].lRecipients[recipient])
hat: Hat # todo: case 0 ?
sInternalTotal: BigDecimal!
interestEarned: BigDecimal! #in rDAI
interestRedeemed: BigDecimal!
# ---------------------------------- links ----------------------------------
transfers: [LoanTransferred!]! @derivedFrom(field: "loan")
}
Expand Down
93 changes: 75 additions & 18 deletions packages/subgraph/src/mapping.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { BigDecimal, EthereumEvent } from '@graphprotocol/graph-ts';
import {
BigDecimal,
BigInt,
EthereumEvent,
log
} from "@graphprotocol/graph-ts";

import {
RToken,
Expand All @@ -11,7 +16,9 @@ import {
LoansTransferred as LoansTransferredEvent,
OwnershipTransferred as OwnershipTransferredEvent,
Transfer as TransferEvent
} from '../generated/RToken/RToken';
} from "../generated/RToken/RToken";

import { IAllocationStrategy } from "../generated/RToken/IAllocationStrategy";

import {
Transaction,
Expand All @@ -23,15 +30,15 @@ import {
LoanTransferred,
InterestPaid,
HatChanged
} from '../generated/schema';
} from "../generated/schema";

import {
createEventID,
createLoanID,
fetchLoan,
fetchAccount,
logTransaction,
toDai
} from './utils';
} from "./utils";

// let contract = Contract.bind(event.address)
//
Expand Down Expand Up @@ -118,7 +125,7 @@ export function handleHatCreated(event: HatCreatedEvent): void {
account.save();

let hatmembership = new HatMembership(
hat.id.concat('-').concat(i.toString())
hat.id.concat("-").concat(i.toString())
);
hatmembership.hat = hat.id;
hatmembership.account = account.id;
Expand All @@ -130,40 +137,90 @@ export function handleHatCreated(event: HatCreatedEvent): void {
export function handleInterestPaid(event: InterestPaidEvent): void {
// balance is updated by the transfer event
let ev = new InterestPaid(createEventID(event));
let value = toDai(event.params.amount);
ev.transaction = logTransaction(event).id;
ev.account = event.params.recipient.toHex();
ev.value = toDai(event.params.amount);
ev.value = value;
ev.save();

// log.error("====== InterestPaidEvent =======", []);
let recipientAccount = fetchAccount(event.params.recipient.toHex());
let loans = recipientAccount.loansReceived;

// Total all unclaimed interest for relevant loans
let interestSum = BigDecimal.fromString("0");
for (let i = 0; i < loans.length; ++i) {
let loan = Loan.load(loans[i]);
if (loan.amount === BigDecimal.fromString("0")) return;
let unredeemedInterest = loan.interestEarned - loan.interestRedeemed;
interestSum = interestSum + unredeemedInterest;
}

let rToken = RToken.bind(event.address);
let savingAssetConversionRate = rToken.savingAssetConversionRate();
let iasAddress = rToken.getCurrentAllocationStrategy();
let ias = IAllocationStrategy.bind(iasAddress);
let exchangeRateStored = ias.exchangeRateStored();

let newInterestEarned = value - interestSum;

for (let i = 0; i < loans.length; ++i) {
let loan = Loan.load(loans[i]);
if (loan.amount === BigDecimal.fromString("0")) return;

// Get the relative proportion of this loan to others
let proportion = loan.interestEarned / interestSum;
// Calculate the proportion of new interest from this loan & update the loan
let interestEarnedProportion = newInterestEarned * proportion;
loan.interestEarned = loan.interestEarned + interestEarnedProportion;
loan.interestRedeemed = loan.interestRedeemed + interestEarnedProportion;
// calculate the proportion of new interest in sInternal & update the loan
let interestEarnedInS =
(value * toDai(savingAssetConversionRate)) / toDai(exchangeRateStored);
loan.sInternalTotal = loan.sInternalTotal - interestEarnedInS * proportion;
loan.save();
}
}

export function handleLoansTransferred(event: LoansTransferredEvent): void {
let ownerAccount = fetchAccount(event.params.owner.toHex());
ownerAccount.save();

let recipientAccount = fetchAccount(event.params.recipient.toHex());
recipientAccount.save();

let id = createLoanID(ownerAccount.id, recipientAccount.id);
let loan = fetchLoan(ownerAccount.id, recipientAccount.id);

let delta = event.params.isDistribution
? toDai(event.params.redeemableAmount)
: -toDai(event.params.redeemableAmount);

let loan = Loan.load(id);
if (loan == null) {
loan = new Loan(id);
loan.owner = ownerAccount.id;
loan.recipient = recipientAccount.id;
loan.amount = BigDecimal.fromString('0');
}
let rToken = RToken.bind(event.address);
let savingAssetConversionRate = rToken.savingAssetConversionRate();
let iasAddress = rToken.getCurrentAllocationStrategy();
let ias = IAllocationStrategy.bind(iasAddress);
let exchangeRateStored = ias.exchangeRateStored();
let sInternal = toDai(event.params.internalSavingsAmount);

loan.sInternalTotal = sInternal + loan.sInternalTotal;
loan.hat = event.params.hatId.toString();
loan.amount = loan.amount + delta;
loan.save();
let interest =
(loan.sInternalTotal * toDai(exchangeRateStored)) /
toDai(savingAssetConversionRate);

let ev = new LoanTransferred(createEventID(event));
ev.transaction = logTransaction(event).id;
ev.loan = id;
ev.loan = loan.id;
ev.value = delta;
ev.save();

let iP = rToken.interestPayableOf(event.params.recipient);
let interestEarned = interest - loan.amount;
if (event.params.isDistribution) {
// Not a redeem event
loan.interestEarned = interest - loan.amount;
}
loan.save();
}

export function handleTransfer(event: TransferEvent): void {
Expand Down
Loading