Skip to content

Commit

Permalink
Add updateOracle rpc (#341)
Browse files Browse the repository at this point in the history
  • Loading branch information
jingyi2811 authored Jun 7, 2021
1 parent 19fa41e commit 2f56708
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { RpcApiError } from '../../../src'
import { UTXO } from '../../../src/category/oracle'

describe('Oracle', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)

beforeAll(async () => {
await container.start()
await container.waitForReady()
await container.waitForWalletCoinbaseMaturity()
})

afterAll(async () => {
await container.stop()
})

it('should updateOracle', async () => {
// Appoint oracle
const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [await container.getNewAddress(), appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: 'FB', currency: 'CNY' },
{ token: 'MSFT', currency: 'SGD' }
]

await client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2
})

await container.generate(1)

const data = await container.call('getoracledata', [oracleid])

expect(data).toStrictEqual({
weightage: 2,
oracleid,
address: expect.any(String),
priceFeeds: updateOraclePriceFeeds,
tokenPrices: []
})
})

it('should not updateOracle for invalid oracleid', async () => {
const oracleid = '8430ac5711d78dc6f98591e144916d27f80952271c62cc15410f878d9b08300d'

const priceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const promise = client.oracle.updateOracle(oracleid, await container.getNewAddress(), { priceFeeds, weightage: 1 })

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow(`RpcApiError: 'Test UpdateOracleAppointTx execution failed:\noracle <${oracleid as string}> not found', code: -32600, method: updateoracle`)
})

it('should updateOracle using same tokens and currencies', async () => {
// Appoint oracle
const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [await container.getNewAddress(), appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: 'CNY', currency: 'FB' },
{ token: 'CNY', currency: 'FB' }
]

await client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2
})

await container.generate(1)

const data = await container.call('getoracledata', [oracleid])

expect(data.priceFeeds).toStrictEqual([{ token: 'CNY', currency: 'FB' }])
})

it('should updateOracle using random tokens and currencies with 1 letter', async () => {
// Appoint oracle
const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [await container.getNewAddress(), appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: 'A', currency: 'C' },
{ token: 'B', currency: 'D' }
]

await client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2
})

await container.generate(1)

const data = await container.call('getoracledata', [oracleid])

expect(data.priceFeeds).toStrictEqual(updateOraclePriceFeeds)
})

it('should updateOracle using random tokens and currencies with 15 letters', async () => {
// Appoint oracle
const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [await container.getNewAddress(), appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: '123456789012345', currency: '123456789012345' },
{ token: 'ABCDEFGHIJKLMNO', currency: 'ABCDEFGHIJKLMNO' }
]

await client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2
})

await container.generate(1)

const data = await container.call('getoracledata', [oracleid])

expect(data.priceFeeds).toStrictEqual([
{ token: '12345678', currency: '12345678' },
{ token: 'ABCDEFGH', currency: 'ABCDEFGH' }
])
})

it('should updateOracle with utxos', async () => {
// Appoint oracle
const address = await container.getNewAddress()

const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [address, appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: 'FB', currency: 'CNY' },
{ token: 'MSFT', currency: 'SGD' }
]

const utxos = await container.call('listunspent', [1, 9999999, [address], true])
const inputs: UTXO[] = utxos.map((utxo: UTXO) => {
return {
txid: utxo.txid,
vout: utxo.vout
}
})

await client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2,
utxos: inputs
})

await container.generate(1)

const data = await container.call('getoracledata', [oracleid])

expect(data).toStrictEqual({
weightage: 2,
oracleid,
address: expect.any(String),
priceFeeds: updateOraclePriceFeeds,
tokenPrices: []
})
})

it('should not updateOracle with arbitrary utxos', async () => {
// Appoint oracle
const appointOraclePriceFeeds = [
{ token: 'APPLE', currency: 'EUR' },
{ token: 'TESLA', currency: 'USD' }
]

const oracleid = await container.call('appointoracle', [await container.getNewAddress(), appointOraclePriceFeeds, 1])

await container.generate(1)

// Update oracle
const updateOraclePriceFeeds = [
{ token: 'FB', currency: 'CNY' },
{ token: 'MSFT', currency: 'SGD' }
]

const { txid, vout } = await container.fundAddress(await container.getNewAddress(), 10)
const promise = client.oracle.updateOracle(oracleid, await container.getNewAddress(), {
priceFeeds: updateOraclePriceFeeds,
weightage: 2,
utxos: [{ txid, vout }]
})

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('RpcApiError: \'Test UpdateOracleAppointTx execution failed:\ntx not from foundation member\', code: -32600, method: updateoracle')
})
})
36 changes: 30 additions & 6 deletions packages/jellyfish-api-core/src/category/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Oracle {
* @param {UTXO[]} [options.utxos = []]
* @param {string} [options.utxos.txid]
* @param {number} [options.utxos.vout]
* @return {Promise<string>} oracleid
* @return {Promise<string>} oracleid, also the txn id for txn created to appoint oracle
*/
async appointOracle (address: string, priceFeeds: PriceFeed[], options: AppointOracleOptions = {}): Promise<string> {
const { utxos = [] } = options
Expand All @@ -34,23 +34,47 @@ export class Oracle {
* @param {UTXO[]} [utxos = []]
* @param {string} [utxos.txid]
* @param {number} [utxos.vout]
* @return {Promise<string>} oracleid
* @return {Promise<string>} txid
*/
async removeOracle (oracleid: string, utxos: UTXO[] = []): Promise<string> {
return await this.client.call('removeoracle', [oracleid, utxos], 'number')
}
}

export interface PriceFeed {
currency: string
token: string
/**
* Update a price oracle for rely of real time price data.
*
* @param {string} oracleid
* @param {string} address
* @param {UpdateOracleOptions} [options]
* @param {PriceFeed[]} options.priceFeeds
* @param {number} options.weightage
* @param {UTXO[]} [options.utxos = []]
* @param {string} [options.utxos.txid]
* @param {number} [options.utxos.vout]
* @return {Promise<string>} txid
*/
async updateOracle (oracleid: string, address: string, options: UpdateOracleOptions = {}): Promise<string> {
const { utxos = [] } = options
return await this.client.call('updateoracle', [oracleid, address, options.priceFeeds, options.weightage, utxos], 'number')
}
}

export interface AppointOracleOptions {
weightage?: number
utxos?: UTXO[]
}

export interface UpdateOracleOptions {
priceFeeds?: PriceFeed[]
weightage?: number
utxos?: UTXO[]
}

export interface PriceFeed {
currency: string
token: string
}

export interface UTXO {
txid: string
vout: number
Expand Down
26 changes: 26 additions & 0 deletions website/docs/jellyfish/api/oracle.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,29 @@ interface UTXO {
vout: number
}
```

## updateOracle

Update a price oracle for rely of real time price data.

```ts title="client.oracle.updateOracle()"
interface oracle {
updateOracle (oracleid: string, address: string, options: UpdateOracleOptions = {}): Promise<string>
}

interface UpdateOracleOptions {
priceFeeds?: PriceFeed[]
weightage?: number
utxos?: UTXO[]
}

interface PriceFeed {
currency: string
token: string
}

interface UTXO {
txid: string
vout: number
}
```

0 comments on commit 2f56708

Please sign in to comment.