Skip to content
Closed
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
49 changes: 49 additions & 0 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,32 @@
</v-list-item-content>
</v-list-item>
</v-list-group>

<v-list-group>
<template #activator>
<v-list-item-title>CPS Trade API</v-list-item-title>
</template>

<v-list-item to="/debug/cps" router exact>
<v-list-item-content>
<v-list-item-title class="list-items pl-2">
Overview
</v-list-item-title>
</v-list-item-content>
</v-list-item>

<v-list-item
v-for="(item, i) in cpsTradesLinks"
:key="`cpsTradesLinks-${i}`"
:to="item.to"
router
exact
>
<v-list-item-content>
<v-list-item-title class="list-items pl-2" v-text="item.title" />
</v-list-item-content>
</v-list-item>
</v-list-group>
</v-list>
</v-navigation-drawer>
<v-app-bar clipped-left fixed app dark color="primary" dense>
Expand Down Expand Up @@ -650,6 +676,29 @@ export default class DefaultLayoutsClass extends Vue {
},
]

cpsTradesLinks = [
{
title: 'POST /cps/quotes',
to: '/debug/cps/quote',
},
{
title: 'POST /cps/trades',
to: '/debug/cps/create',
},
{
title: 'GET /cps/trades',
to: '/debug/cps/fetch',
},
{
title: 'GET /cps/trades/{id}',
to: '/debug/cps/details',
},
{
title: 'POST /cps/signatures',
to: '/debug/cps/signature',
},
]

miniVariant = false
right = true
showRightDrawer = false
Expand Down
131 changes: 131 additions & 0 deletions lib/cpsTradesApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import get from 'lodash/get'
import axios from 'axios'

import { getAPIHostname } from './apiTarget'

export interface CreateCpsQuotePayload {
from: {
amount?: number
currency: string
}
to: {
amount?: number
currency: string
}
}

export interface CreateCpsTradePayload {
idempotencyKey: string
quoteId: string
}

export interface Consideration {
quoteId: string
base: string
quote: string
quoteAmount: number
baseAmount: number
maturity: number
}

export interface PiFXTraderDetails {
recipient: string
deadline: number
nonce: number
consideration: Consideration
}

export interface CreatePiFXSignaturePayload {
tradeId: string
type: string
address: string
details: PiFXTraderDetails
signature: string
}

const instance = axios.create({
baseURL: getAPIHostname(),
})

const CPS_TRADES_PATH = '/v1/exchange/cps/trades'
const CPS_QUOTES_PATH = '/v1/exchange/cps/quotes'
const CPS_SIGNATURES_PATH = '/v1/exchange/cps/signatures'

/**
* Global error handler:
* Intercepts all axios reponses and maps
* to errorHandler object
*/
instance.interceptors.response.use(
function (response) {
if (get(response, 'data.data')) {
return response.data.data
}
return response
},
function (error) {
let response = get(error, 'response')
if (!response) {
response = error.toJSON()
}
return Promise.reject(response)
}
)

/** Returns the axios instance */
function getInstance() {
return instance
}

/**
* Create CPS Quote
*/
function createQuote(payload: CreateCpsQuotePayload) {
if (!payload.from.amount) {
delete payload.from.amount
}
if (!payload.to.amount) {
delete payload.to.amount
}

return instance.post(CPS_QUOTES_PATH, payload)
}

/**
* Create CPS Trade
*/
function createTrade(payload: CreateCpsTradePayload) {
return instance.post(CPS_TRADES_PATH, payload)
}

/**
* Get CPS Trades
*/
function getTrades() {
return instance.get(CPS_TRADES_PATH)
}

/**
* Get CPS Trade
*/
function getTrade(tradeId: string) {
const url = `${CPS_TRADES_PATH}/${tradeId}`

return instance.get(url)
}

/**
* Register CPS Signature
*/
function registerSignature(payload: CreatePiFXSignaturePayload) {
return instance.post(CPS_SIGNATURES_PATH, payload)
}

export default {
getInstance,
createQuote,
createTrade,
getTrades,
getTrade,
registerSignature,
}
2 changes: 2 additions & 0 deletions nuxt.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export default {
'~/plugins/walletsApi',
'~/plugins/paymentIntentsApi',
'~/plugins/mocksApi',
'~/plugins/tradesApi',
'~/plugins/cpsTradesApi',
'~/plugins/businessAccount/addressesApi',
'~/plugins/businessAccount/balancesApi',
'~/plugins/businessAccount/bankAccountsApi',
Expand Down
95 changes: 95 additions & 0 deletions pages/debug/cps/create.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<template>
<v-layout>
<v-row>
<v-col cols="12" md="4">
<v-form v-model="validForm">
<v-text-field
v-model="formData.quoteId"
:rules="[required]"
hint="ID of CPS FX quote to trade on"
label="Quote ID"
/>
<v-btn
depressed
class="mb-7"
color="primary"
:loading="loading"
:disabled="!validForm || loading"
@click.prevent="makeApiCall"
>
Make api call
</v-btn>
</v-form>
</v-col>
<v-col cols="12" md="8">
<RequestInfo
:url="requestUrl"
:payload="payload"
:response="response"
/>
</v-col>
</v-row>
<ErrorSheet
:error="error"
:show-error="showError"
@onChange="onErrorSheetClosed"
/>
</v-layout>
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import { mapGetters } from 'vuex'
import { v4 as uuidv4 } from 'uuid'
import RequestInfo from '@/components/RequestInfo.vue'
import ErrorSheet from '@/components/ErrorSheet.vue'
import { CreateCpsTradePayload } from '~/lib/cpsTradesApi'

@Component({
components: {
RequestInfo,
ErrorSheet,
},
computed: {
...mapGetters({
payload: 'getRequestPayload',
response: 'getRequestResponse',
requestUrl: 'getRequestUrl',
}),
},
})
export default class CreateCpsTradeClass extends Vue {
validForm: boolean = false
formData = {
quoteId: '',
}

required = (v: string) => !!v || 'Field is required'
error = {}
loading = false
showError = false

onErrorSheetClosed() {
this.error = {}
this.showError = false
}

async makeApiCall() {
this.loading = true

const payload: CreateCpsTradePayload = {
idempotencyKey: uuidv4(),
quoteId: this.formData.quoteId,
}

try {
await this.$cpsTradesApi.createTrade(payload)
} catch (error) {
this.error = error
this.showError = true
} finally {
this.loading = false
}
}
}
</script>
88 changes: 88 additions & 0 deletions pages/debug/cps/details.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<template>
<v-layout>
<v-row>
<v-col cols="12" md="4">
<v-form v-model="validForm">
<v-text-field
v-model="formData.tradeId"
:rules="[required]"
label="CPS Trade ID"
/>
<v-btn
depressed
class="mb-7"
color="primary"
:loading="loading"
:disabled="!validForm || loading"
@click.prevent="makeApiCall"
>
Make api call
</v-btn>
</v-form>
</v-col>
<v-col cols="12" md="8">
<RequestInfo
:url="requestUrl"
:payload="payload"
:response="response"
/>
</v-col>
</v-row>
<ErrorSheet
:error="error"
:show-error="showError"
@onChange="onErrorSheetClosed"
/>
</v-layout>
</template>

<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import { mapGetters } from 'vuex'
import RequestInfo from '@/components/RequestInfo.vue'
import ErrorSheet from '@/components/ErrorSheet.vue'

@Component({
components: {
RequestInfo,
ErrorSheet,
},
computed: {
...mapGetters({
payload: 'getRequestPayload',
response: 'getRequestResponse',
requestUrl: 'getRequestUrl',
isMarketplace: 'isMarketplace',
}),
},
})
export default class GetCpsTradeDetailsClass extends Vue {
validForm: boolean = false
formData = {
tradeId: '',
}

required = (v: string) => !!v || 'Field is required'
error = {}
loading = false
showError = false

onErrorSheetClosed() {
this.error = {}
this.showError = false
}

async makeApiCall() {
this.loading = true

try {
await this.$cpsTradesApi.getTrade(this.formData.tradeId)
} catch (error) {
this.error = error
this.showError = true
} finally {
this.loading = false
}
}
}
</script>
Loading
Loading