Skip to content

Commit

Permalink
Merge pull request #276 from interlay/dan/oracle-names
Browse files Browse the repository at this point in the history
feat(oracle): Return names mapped by id
  • Loading branch information
daniel-savu authored Mar 26, 2021
2 parents f54c863 + c52a2e0 commit 0dad6cd
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 65 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@interlay/polkabtc",
"version": "0.11.1",
"version": "0.11.2",
"description": "JavaScript library to interact with PolkaBTC",
"main": "build/index.js",
"typings": "build/index.d.ts",
Expand Down
48 changes: 21 additions & 27 deletions src/parachain/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,14 @@ import {
decodeFixedPointType,
Transaction,
encodeUnsignedFixedPoint,
ACCOUNT_NOT_SET_ERROR_MESSAGE
ACCOUNT_NOT_SET_ERROR_MESSAGE,
storageKeyToFirstInner
} from "../utils";
import Big from "big.js";
import { AddressOrPair } from "@polkadot/api/types";

const defaultFeedName = "DOT/BTC";

export type OracleInfo = {
exchangeRate: Big;
feed: string;
names: Array<string>;
online: boolean;
lastUpdate: Date;
};

export type BtcTxFees = {
fast: number;
half: number;
Expand Down Expand Up @@ -53,17 +46,13 @@ export interface OracleAPI {
*/
getLastExchangeRateTime(): Promise<Date>;
/**
* @returns An array with the oracle names
* @returns A map from the oracle's account id to its name
*/
getOracleNames(): Promise<Array<string>>;
getSourcesById(): Promise<Map<string, string>>;
/**
* @returns Boolean value indicating whether the oracle is online
*/
isOnline(): Promise<boolean>;
/**
* @returns An object of type OracleInfo
*/
getInfo(): Promise<OracleInfo>;
/**
* Send a transaction to set the DOT/BTC exchange rate
* @param exchangeRate The rate to set
Expand All @@ -89,6 +78,11 @@ export interface OracleAPI {
* @returns Convert a Satoshi amount to Planck
*/
convertSatoshiToPlanck(satoshi: PolkaBTC): Promise<Big>;
/**
* @returns The period of time (in milliseconds) after an oracle's last submission
* during which it is considered online
*/
getOnlineTimeout(): Promise<number>;
}

export class DefaultOracleAPI implements OracleAPI {
Expand All @@ -98,16 +92,6 @@ export class DefaultOracleAPI implements OracleAPI {
this.transaction = new Transaction(api);
}

async getInfo(): Promise<OracleInfo> {
return {
exchangeRate: await this.getExchangeRate(),
feed: await this.getFeed(),
names: await this.getOracleNames(),
online: await this.isOnline(),
lastUpdate: await this.getLastExchangeRateTime(),
};
}

async convertSatoshiToPlanck(satoshi: PolkaBTC): Promise<Big> {
const planckPerSatoshi = await this.getRawExchangeRate();
const amountSatoshiBig = new Big(satoshi.toString());
Expand All @@ -119,6 +103,12 @@ export class DefaultOracleAPI implements OracleAPI {
return new Big(this.convertFromRawExchangeRate(rawRate.toString()));
}

async getOnlineTimeout(): Promise<number> {
const head = await this.api.rpc.chain.getFinalizedHead();
const moment = await this.api.query.exchangeRateOracle.maxDelay.at(head);
return moment.toNumber();
}

async getRawExchangeRate(): Promise<Big> {
const head = await this.api.rpc.chain.getFinalizedHead();
const encodedRawRate = await this.api.query.exchangeRateOracle.exchangeRate.at(head);
Expand Down Expand Up @@ -157,10 +147,14 @@ export class DefaultOracleAPI implements OracleAPI {
await this.transaction.sendLogged(tx, this.account, this.api.events.exchangeRateOracle.SetBtcTxFeesPerByte);
}

async getOracleNames(): Promise<Array<string>> {
async getSourcesById(): Promise<Map<string, string>> {
const head = await this.api.rpc.chain.getFinalizedHead();
const oracles = await this.api.query.exchangeRateOracle.authorizedOracles.entriesAt(head);
return oracles.map((v) => v[1].toUtf8());
const nameMap = new Map<string, string>();
oracles.forEach((oracle) =>
nameMap.set(storageKeyToFirstInner(oracle[0]).toString(), oracle[1].toUtf8())
);
return nameMap;
}

getFeed(): Promise<string> {
Expand Down
9 changes: 2 additions & 7 deletions src/parachain/replace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { ApiPromise } from "@polkadot/api";
import { PolkaBTC, ReplaceRequest } from "../interfaces/default";
import { BlockNumber } from "@polkadot/types/interfaces/runtime";
import { Hash } from "@polkadot/types/interfaces";
import { StorageKey } from "@polkadot/types/primitive/StorageKey";
import { Network } from "bitcoinjs-lib";
import { ACCOUNT_NOT_SET_ERROR_MESSAGE, encodeBtcAddress, Transaction } from "../utils";
import { ACCOUNT_NOT_SET_ERROR_MESSAGE, encodeBtcAddress, storageKeyToFirstInner, Transaction } from "../utils";
import { H256 } from "@polkadot/types/interfaces";
import { AddressOrPair } from "@polkadot/api/submittable/types";
import { DefaultFeeAPI, FeeAPI } from "./fee";
Expand Down Expand Up @@ -147,10 +146,6 @@ export class DefaultReplaceAPI implements ReplaceAPI {
.map((req: ReplaceRequest) => encodeReplaceRequest(req, this.btcNetwork));
}

private storageKeyToIdString(s: StorageKey<[H256]>): H256 {
return s.args[0];
}

async map(): Promise<Map<H256, ReplaceRequestExt>> {
const head = await this.api.rpc.chain.getFinalizedHead();
const redeemRequests = await this.api.query.replace.replaceRequests.entriesAt(head);
Expand All @@ -161,7 +156,7 @@ export class DefaultReplaceAPI implements ReplaceAPI {
return { id: v[0], req: v[1].unwrap() };
})
.forEach(({ id, req }) => {
redeemRequestMap.set(this.storageKeyToIdString(id), encodeReplaceRequest(req, this.btcNetwork));
redeemRequestMap.set(storageKeyToFirstInner(id), encodeReplaceRequest(req, this.btcNetwork));
});
return redeemRequestMap;
}
Expand Down
6 changes: 6 additions & 0 deletions src/utils/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import Big from "big.js";
import { ApiPromise } from "@polkadot/api";
import type { Struct } from "@polkadot/types";
import { Network } from "bitcoinjs-lib";
import { StorageKey } from "@polkadot/types/primitive/StorageKey";
import { H256 } from "@polkadot/types/interfaces";

/**
* Converts endianness of a Uint8Array
Expand Down Expand Up @@ -69,6 +71,10 @@ export function encodeUnsignedFixedPoint(api: ApiPromise, x: string): UnsignedFi
return api.createType("FixedU128", xScaled.toFixed());
}

export function storageKeyToFirstInner(s: StorageKey<[H256]>): H256 {
return s.args[0];
}

export interface DecodedRequest extends Struct {
readonly btc_address: BtcAddress;
}
Expand Down
18 changes: 9 additions & 9 deletions test/integration/parachain/oracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ import { defaultParachainEndpoint } from "../../config";
describe("OracleAPI", () => {
let api: ApiPromise;
let oracle: OracleAPI;
let alice: KeyringPair;
let bob: KeyringPair;

before(async () => {
api = await createPolkadotAPI(defaultParachainEndpoint);
const keyring = new Keyring({ type: "sr25519" });
alice = keyring.addFromUri("//Alice");
bob = keyring.addFromUri("//Bob");
});

Expand All @@ -28,13 +26,6 @@ describe("OracleAPI", () => {
});

describe("Oracle", () => {
it("should return oracle info", async () => {
const info = await oracle.getInfo();
assert.equal(info.names[0], "Bob");
assert.isTrue(info.online);
assert.equal(info.feed, "DOT/BTC");
});

it("[docker-compose initial setup] should set a rate of 3855.23187", async () => {
const exchangeRate = await oracle.getExchangeRate();
assert.equal(exchangeRate.toString(), "3855.23187");
Expand All @@ -61,5 +52,14 @@ describe("OracleAPI", () => {

await oracle.setBtcTxFeesPerByte(prev);
});

it("should get names by id", async () => {
const expectedSources = new Map<string, string>();
expectedSources.set("5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", "Bob");
const sources = await oracle.getSourcesById();
for (const entry of sources.entries()) {
assert.equal(entry[1], expectedSources.get(entry[0]));
}
});
});
});
22 changes: 7 additions & 15 deletions test/mock/parachain/oracle.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import { AddressOrPair } from "@polkadot/api/types";
import { OracleAPI } from "../../../src/parachain";
import { BtcTxFees, OracleInfo } from "../../../src/parachain/oracle";
import { BtcTxFees } from "../../../src/parachain/oracle";
import Big from "big.js";
import { PolkaBTC } from "@interlay/polkabtc/interfaces";

export class MockOracleAPI implements OracleAPI {
convertSatoshiToPlanck(_satoshi: PolkaBTC): Promise<Big> {
throw new Error("Method not implemented.");
getOnlineTimeout(): Promise<number> {
return Promise.resolve(100);
}

async getInfo(): Promise<OracleInfo> {
const oracle_info: OracleInfo = {
exchangeRate: await this.getExchangeRate(),
feed: await this.getFeed(),
names: await this.getOracleNames(),
online: await this.isOnline(),
lastUpdate: await this.getLastExchangeRateTime(),
};

return Promise.resolve(oracle_info);
convertSatoshiToPlanck(_satoshi: PolkaBTC): Promise<Big> {
throw new Error("Method not implemented.");
}

getExchangeRate(): Promise<Big> {
Expand All @@ -33,8 +25,8 @@ export class MockOracleAPI implements OracleAPI {
return Promise.resolve({ fast: 500, half: 300, hour: 200 });
}

getOracleNames(): Promise<Array<string>> {
return Promise.resolve(["ChainLink"]);
getSourcesById(): Promise<Map<string, string>> {
return Promise.resolve(new Map());
}

getFeed(): Promise<string> {
Expand Down
6 changes: 0 additions & 6 deletions test/unit/parachain/oracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,6 @@ describe("oracle", () => {
});
});

describe("oracleName", () => {
it("should return name", () => {
return assert.eventually.deepEqual(oracle.getOracleNames(), ["test"]);
});
});

describe("convert exchange rate", () => {
it("should return the correct rate", () => {
const oracleProto = Object.getPrototypeOf(oracle);
Expand Down

0 comments on commit 0dad6cd

Please sign in to comment.