Skip to content

Commit

Permalink
Interfaceicx_claimdfchtlc RPC (#406)
Browse files Browse the repository at this point in the history
* Added ICXOrderBook.claimDFCHTLC() function, testing and documentation

* Refactored code.

* Refactored code.

* Refactored code.
  • Loading branch information
surangap authored Jul 8, 2021
1 parent 6420b02 commit 3421144
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { ContainerAdapterClient } from '../../container_adapter_client'
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import {
ICXClaimDFCHTLCInfo, ICXDFCHTLCInfo, ICXEXTHTLCInfo, ICXHTLCStatus,
ICXHTLCType, ICXListHTLCOptions
} from '../../../src/category/icxorderbook'
import { accountDFI, idDFI, accountBTC, ICXSetup, symbolDFI } from './icx_setup'
import { BigNumber, RpcApiError } from '../../../src'

describe('ICXOrderBook.claimDFCHTLC', () => {
const container = new MasterNodeRegTestContainer()
const client = new ContainerAdapterClient(container)
const icxSetup = new ICXSetup(container, client)

beforeAll(async () => {
await container.start()
await container.waitForReady()
await container.waitForBlock(1)
await icxSetup.createAccounts()
await icxSetup.createBTCToken()
await icxSetup.initializeTokensIds()
await icxSetup.mintBTCtoken(100)
await icxSetup.fundAccount(accountDFI, symbolDFI, 500)
await icxSetup.fundAccount(accountBTC, symbolDFI, 10) // for fee
await icxSetup.createBTCDFIPool()
await icxSetup.addLiquidityToBTCDFIPool(1, 100)
await icxSetup.setTakerFee(0.001)
})

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

afterEach(async () => {
// enable this after #ain/583
// await icxSetup.closeAllOpenOffers()
})

it('should claim DFC HTLC for DFI sell order', async () => {
const { createOrderTxId } = await icxSetup.createDFISellOrder('BTC', accountDFI, '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', new BigNumber(15), new BigNumber(0.01))
const { makeOfferTxId } = await icxSetup.createDFIBuyOffer(createOrderTxId, new BigNumber(0.10), accountBTC)
const { DFCHTLCTxId } = await icxSetup.createDFCHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', 500)
await icxSetup.submitExtHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(0.10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', 15)

const accountDFIBeforeClaim: Record<string, BigNumber> = await client.call('getaccount', [accountDFI, {}, true], 'bignumber')
const accountBTCBeforeClaim: Record<string, BigNumber> = await client.call('getaccount', [accountBTC, {}, true], 'bignumber')
// claim
const { txid: claimTxId } = await client.icxorderbook.claimDFCHTLC(DFCHTLCTxId, 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef')
await container.generate(1)

// List htlc and check
const listHTLCOptions: ICXListHTLCOptions = {
offerTx: makeOfferTxId,
closed: true
}
const HTLCs: Record<string, ICXDFCHTLCInfo | ICXEXTHTLCInfo | ICXClaimDFCHTLCInfo> = await client.call('icx_listhtlcs', [listHTLCOptions], 'bignumber')
expect(Object.keys(HTLCs).length).toBe(4) // extra entry for the warning text returned by the RPC atm.
// we have a common field "type", use that to narrow down the record
if (HTLCs[claimTxId].type === ICXHTLCType.CLAIM_DFC) {
// ICXClaimDFCHTLCInfo cast
const ClaimHTLCInfo: ICXClaimDFCHTLCInfo = HTLCs[claimTxId] as ICXClaimDFCHTLCInfo
expect(ClaimHTLCInfo.dfchtlcTx).toStrictEqual(DFCHTLCTxId)
expect(ClaimHTLCInfo.seed).toStrictEqual('f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef')
}

// check HTLC DFCHTLCTxId is in claimed status
if (HTLCs[DFCHTLCTxId].type === ICXHTLCType.DFC) {
// ICXDFCHTLCInfo cast
const DFCHTLCInfo: ICXDFCHTLCInfo = HTLCs[DFCHTLCTxId] as ICXDFCHTLCInfo
expect(DFCHTLCInfo.status).toStrictEqual(ICXHTLCStatus.CLAIMED)
}

const accountDFIAfterClaim: Record<string, BigNumber> = await client.call('getaccount', [accountDFI, {}, true], 'bignumber')
const accountBTCAfterClaim: Record<string, BigNumber> = await client.call('getaccount', [accountBTC, {}, true], 'bignumber')

// maker should get incentive + maker deposit and taker should get amount in DFCHTLCTxId HTLC - takerfee
expect(accountDFIAfterClaim[idDFI]).toStrictEqual(accountDFIBeforeClaim[idDFI].plus(0.010).plus(0.00250))
expect(accountBTCAfterClaim[idDFI]).toStrictEqual(accountBTCBeforeClaim[idDFI].plus(10))
})

it('should claim DFC HTLC with input utxos', async () => {
const { createOrderTxId } = await icxSetup.createDFISellOrder('BTC', accountDFI, '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', new BigNumber(15), new BigNumber(0.01))
const { makeOfferTxId } = await icxSetup.createDFIBuyOffer(createOrderTxId, new BigNumber(0.10), accountBTC)
const { DFCHTLCTxId } = await icxSetup.createDFCHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', 500)
await icxSetup.submitExtHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(0.10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', 15)

const accountDFIBeforeClaim: Record<string, BigNumber> = await client.call('getaccount', [accountDFI, {}, true], 'bignumber')
const accountBTCBeforeClaim: Record<string, BigNumber> = await client.call('getaccount', [accountBTC, {}, true], 'bignumber')

// input utxos
const inputUTXOs = await container.fundAddress(accountBTC, 10)
// claim
const claimTxId = (await client.icxorderbook.claimDFCHTLC(DFCHTLCTxId, 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef', [inputUTXOs])).txid
await container.generate(1)

const rawtx = await container.call('getrawtransaction', [claimTxId, true])
expect(rawtx.vin[0].txid).toStrictEqual(inputUTXOs.txid)
expect(rawtx.vin[0].vout).toStrictEqual(inputUTXOs.vout)

// List htlc and check
const listHTLCOptions: ICXListHTLCOptions = {
offerTx: makeOfferTxId,
closed: true
}
const HTLCs: Record<string, ICXDFCHTLCInfo | ICXEXTHTLCInfo | ICXClaimDFCHTLCInfo> = await client.call('icx_listhtlcs', [listHTLCOptions], 'bignumber')
expect(Object.keys(HTLCs).length).toBe(4) // extra entry for the warning text returned by the RPC atm.
// we have a common field "type", use that to narrow down the record
if (HTLCs[claimTxId].type === ICXHTLCType.CLAIM_DFC) {
// ICXClaimDFCHTLCInfo cast
const ClaimHTLCInfo: ICXClaimDFCHTLCInfo = HTLCs[claimTxId] as ICXClaimDFCHTLCInfo
expect(ClaimHTLCInfo.dfchtlcTx).toStrictEqual(DFCHTLCTxId)
expect(ClaimHTLCInfo.seed).toStrictEqual('f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef')
}

// check HTLC DFCHTLCTxId is in claimed status
if (HTLCs[DFCHTLCTxId].type === ICXHTLCType.DFC) {
// ICXDFCHTLCInfo cast
const DFCHTLCInfo: ICXDFCHTLCInfo = HTLCs[DFCHTLCTxId] as ICXDFCHTLCInfo
expect(DFCHTLCInfo.status).toStrictEqual(ICXHTLCStatus.CLAIMED)
}

const accountDFIAfterClaim: Record<string, BigNumber> = await client.call('getaccount', [accountDFI, {}, true], 'bignumber')
const accountBTCAfterClaim: Record<string, BigNumber> = await client.call('getaccount', [accountBTC, {}, true], 'bignumber')

// maker should get incentive + maker deposit and taker should get amount in DFCHTLCTxId HTLC - takerfee
expect(accountDFIAfterClaim[idDFI]).toStrictEqual(accountDFIBeforeClaim[idDFI].plus(0.010).plus(0.00250))
expect(accountBTCAfterClaim[idDFI]).toStrictEqual(accountBTCBeforeClaim[idDFI].plus(10))
})

it('should return an error when try to claim DFC HTLC with invalid transaction id', async () => {
const { createOrderTxId } = await icxSetup.createDFISellOrder('BTC', accountDFI, '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', new BigNumber(15), new BigNumber(0.01))
const { makeOfferTxId } = await icxSetup.createDFIBuyOffer(createOrderTxId, new BigNumber(0.10), accountBTC)
await icxSetup.createDFCHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', 500)
await icxSetup.submitExtHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(0.10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', 15)

// claim with invalid DFC HTLC tx id "123"
const promise = client.icxorderbook.claimDFCHTLC('123', 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef')
await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('RpcApiError: \'Test ICXClaimDFCHTLCTx execution failed:\ndfc htlc with creation tx 0000000000000000000000000000000000000000000000000000000000000123 does not exists!\', code: -32600, method: icx_claimdfchtlc')

// claim with invalid DFC HTLC tx id "INVALID_DFC_HTLC_TX_ID"
const promise2 = client.icxorderbook.claimDFCHTLC('INVALID_DFC_HTLC_TX_ID', 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef')
await expect(promise2).rejects.toThrow(RpcApiError)
await expect(promise2).rejects.toThrow('RpcApiError: \'Test ICXClaimDFCHTLCTx execution failed:\ndfc htlc with creation tx 0000000000000000000000000000000000000000000000000000000000000000 does not exists!\', code: -32600, method: icx_claimdfchtlc')
})

it('should return an error when try to claim DFC HTLC with invalid seed', async () => {
const { createOrderTxId } = await icxSetup.createDFISellOrder('BTC', accountDFI, '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', new BigNumber(15), new BigNumber(0.01))
const { makeOfferTxId } = await icxSetup.createDFIBuyOffer(createOrderTxId, new BigNumber(0.10), accountBTC)
const { DFCHTLCTxId } = await icxSetup.createDFCHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', 500)
await icxSetup.submitExtHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(0.10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
'13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', 15)

// claim with invalid seed "INVALID_SEED"
const promise = client.icxorderbook.claimDFCHTLC(DFCHTLCTxId, 'INVALID_SEED')
await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('RpcApiError: \'Test ICXClaimDFCHTLCTx execution failed:\nhash generated from given seed is different than in dfc htlc: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 - 957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220!\', code: -32600, method: icx_claimdfchtlc')
})

// NOTE(surangap): Why this test is failing. should not be able to claim with arbitary utxos?
// it('should return an error when try to claim DFC HTLC with arbitary input utxos', async () => {
// const { createOrderTxId } = await icxSetup.createDFISellOrder('BTC', accountDFI, '037f9563f30c609b19fd435a19b8bde7d6db703012ba1aba72e9f42a87366d1941', new BigNumber(15), new BigNumber(0.01))
// const { makeOfferTxId } = await icxSetup.createDFIBuyOffer(createOrderTxId, new BigNumber(0.10), accountBTC)
// const { DFCHTLCTxId } = await icxSetup.createDFCHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220', 500)
// await icxSetup.submitExtHTLCForDFIBuyOffer(makeOfferTxId, new BigNumber(0.10), '957fc0fd643f605b2938e0631a61529fd70bd35b2162a21d978c41e5241a5220',
// '13sJQ9wBWh8ssihHUgAaCmNWJbBAG5Hr9N', '036494e7c9467c8c7ff3bf29e841907fb0fa24241866569944ea422479ec0e6252', 15)

// // input utxos
// const inputUTXOs = await container.fundAddress(await container.getNewAddress(), 10)
// // claim
// const promise = client.icxorderbook.claimDFCHTLC(DFCHTLCTxId, 'f75a61ad8f7a6e0ab701d5be1f5d4523a9b534571e4e92e0c4610c6a6784ccef', [inputUTXOs])
// await expect(promise).rejects.toThrow(RpcApiError)
// await expect(promise).rejects.toThrow('RpcApiError: \'Test ICXSubmitEXTHTLCTx execution failed:\ntx must have at least one input from offer owner\', code: -32600, method: icx_submitexthtlc')

// // List htlc and check
// const listHTLCOptions: ICXListHTLCOptions = {
// offerTx: makeOfferTxId,
// }
// const HTLCs: Record<string, ICXDFCHTLCInfo | ICXEXTHTLCInfo | ICXClaimDFCHTLCInfo> = await client.call('icx_listhtlcs', [listHTLCOptions], 'bignumber')
// expect(Object.keys(HTLCs).length).toBe(3) // extra entry for the warning text returned by the RPC atm.
// // check HTLC DFCHTLCTxId is still in OPEN status
// if (HTLCs[DFCHTLCTxId].type === ICXHTLCType.DFC) {
// // ICXDFCHTLCInfo cast
// const DFCHTLCInfo: ICXDFCHTLCInfo = HTLCs[DFCHTLCTxId] as ICXDFCHTLCInfo
// expect(DFCHTLCInfo.status).toStrictEqual(ICXHTLCStatus.OPEN)
// }
// })
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { BigNumber } from '@defichain/jellyfish-api-core'
import { ICXOrder, ICXGenericResult, ICXOrderInfo, ICXOfferInfo, ICXOffer, ICXOrderStatus, ICXHTLCType, ICXHTLCStatus, HTLC, ICXClaimDFCHTLCInfo, ICXDFCHTLCInfo, ICXEXTHTLCInfo, ICXListHTLCOptions } from '../../../src/category/icxorderbook'
import {
ICXOrder, ICXGenericResult, ICXOrderInfo, ICXOfferInfo, ICXOffer, ICXOrderStatus, ICXHTLCType, ICXHTLCStatus, HTLC,
ICXClaimDFCHTLCInfo, ICXDFCHTLCInfo, ICXEXTHTLCInfo, ICXListHTLCOptions, ExtHTLC
} from '../../../src/category/icxorderbook'
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { createToken, mintTokens, accountToAccount } from '@defichain/testing'
import { ContainerAdapterClient } from 'jellyfish-api-core/__tests__/container_adapter_client'
Expand Down Expand Up @@ -182,7 +185,7 @@ export class ICXSetup {
}
}

// create and submits DFC HTLC for DFI buy order
// create and submits DFC HTLC for DFI buy offer
async createDFCHTLCForDFIBuyOffer (makeOfferTxId: string, amount: BigNumber, hash: string, timeout: number): Promise<{DFCHTLC: HTLC, DFCHTLCTxId: string}> {
const accountDFIBeforeDFCHTLC: Record<string, BigNumber> = await this.client.call('getaccount', [accountDFI, {}, true], 'bignumber')
// create DFCHTLC - maker
Expand Down Expand Up @@ -213,4 +216,38 @@ export class ICXSetup {
DFCHTLCTxId: DFCHTLCTxId
}
}

// submits ExtHTLC for DFI buy offer
async submitExtHTLCForDFIBuyOffer (makeOfferTxId: string, amount: BigNumber, hash: string, htlcScriptAddress: string, ownerPubkey: string, timeout: number): Promise<{ExtHTLC: ExtHTLC, ExtHTLCTxId: string}> {
const accountBTCBeforeEXTHTLC = await this.client.call('getaccount', [accountBTC, {}, true], 'bignumber')
// submit EXT HTLC - taker
const ExtHTLC: ExtHTLC = {
offerTx: makeOfferTxId,
amount: amount,
hash: hash,
htlcScriptAddress: htlcScriptAddress,
ownerPubkey: ownerPubkey,
timeout: timeout
}
const ExtHTLCTxId = (await this.client.icxorderbook.submitExtHTLC(ExtHTLC)).txid
await this.container.generate(1)

// List htlc
const listHTLCOptions: ICXListHTLCOptions = {
offerTx: makeOfferTxId
}
const HTLCs: Record<string, ICXDFCHTLCInfo | ICXEXTHTLCInfo| ICXClaimDFCHTLCInfo> = await this.client.call('icx_listhtlcs', [listHTLCOptions], 'bignumber')
expect(Object.keys(HTLCs).length).toBe(3) // extra entry for the warning text returned by the RPC atm.
expect((HTLCs[ExtHTLCTxId] as ICXEXTHTLCInfo).type).toStrictEqual(ICXHTLCType.EXTERNAL)
expect((HTLCs[ExtHTLCTxId] as ICXEXTHTLCInfo).status).toStrictEqual(ICXHTLCStatus.OPEN)

const accountBTCAfterEXTHTLC = await this.client.call('getaccount', [accountBTC, {}, true], 'bignumber')
// should have the same balance as accountBTCBeforeEXTHTLC
expect(accountBTCAfterEXTHTLC).toStrictEqual(accountBTCBeforeEXTHTLC)

return {
ExtHTLC: ExtHTLC,
ExtHTLCTxId: ExtHTLCTxId
}
}
}
Loading

0 comments on commit 3421144

Please sign in to comment.