Skip to content

Commit

Permalink
feat: support peer service adding initial amount of liquidity (#1882)
Browse files Browse the repository at this point in the history
* feat(backend): WIP - add feeid to quote table

* refactor(backend): rename quote gql field

* fix(backend): add fee relation

* feat(backend): WIP calculate fees in quote create

* refactor(backend): rename gql quote field

* fix(backend): rm unused with graph fetched

* feat(backend): move fee calculation to quote service

* fix(backend): use quote error

* fix(backend): rm unused fn

* refactor(backend): rename quote send amount value

* refactor(backend): rename quote send amount to debit amount

* refactor(backend): rename out payment send amount to debit

* chore(backend): format

* chore(documentation): update gql docs

* fix(backend,localenv): trx amount

* fix(backend): handle cross currency fee in quote

* refactor: update spec and rename more fields

* fix(backend): outgoing limit test

* refactor(backend): rename limits field

* rename sendAmount to debitAmount

* fix(localenv): postman ecommerce flow

* fix(backend): remove useless checks

* chore(auth): update open-payments package version

* chore(backend): rm commented out code

* fix(auth): migration to rename jsonb col

* chore(auth): format

* feat: add DuplicatePeer error

* fix: pass in peerService to autoPeeringService

* chore: update diff

* chore: add to peer resolver test

* feat: accept peering request

* fix(backend): remove asset join

* refactor(backend): remove variable assignment

* feat: wire up router for auto peering

* fix: update auto peering service

* feat: correct config

* config: update auto peering routes & errors

* chore: prettier

* feat: add createPeerByUrl mutation to schema

* feat: add initiatePeeringRequest method to auto peering service

* feat: add createPeerByUrl resolver

* chore: fix lint

* chore: add generate graphql files

* chore: update graphql schema comment

* fix: add type to /post peer error

* feat: add postman request

* chore: address comments

* Update packages/backend/src/auto-peering/errors.ts

Co-authored-by: Radu-Cristian Popa <[email protected]>

* chore: remove axios from app

* chore: add axios to app

* chore: fix whitespace

* feature(backend): change error

* reactor(documentation): update fee explanation

* feat: use assetId in getByDestinationAddress

* feat: update peer on duplicate peering request

* chore: remove quote url references

* feat: use proper outgoing/incoming http tokens

* feat: update error during auto-peering request

* fix(openapi): missing server warning

* chore: fix route tests

* feat: support maxPacketAmount and name in peering request

* chore: add fields to peering request

* feat: support maxPacketAmount and name in peering request

* feat: default to peer given name if no name provided

* chore: lint

* fix: remove getter

* Temporary fix

* Revert "Temporary fix"

This reverts commit bc7c69a.

* chore(token-introspection): generate types

* chore: update tests

* refactor(documentation): rename sendAmount to debitAmount

* Revert "Merge branch 'bc/1639/add-fees-to-quote' into mk/1802/accept-peering-request"

This reverts commit 8fb0154, reversing
changes made to 9619132.

* ci: test heap bump

* ci: logheapusage

* chore: bump jest

* chore: update tests

* chore: update tests

* Revert "ci: logheapusage"

This reverts commit 66293e5.

* revert fix

* fix: revert pnpm lock

* Update mutation request

* feat: allow knex trx to be used in transfer creation

* feat: add initial amount of liquidity in peer service

* feat: add max amount of liquidity during auto-peering

* feat: support (new) liquidityThreshold param

* chore: lint

* chore: fix test

* Update `createPeer` and `createPeerByUrl` mutations in postman

* Revert "Update `createPeer` and `createPeerByUrl` mutations in postman"

This reverts commit 1b53f92.

* chore: rename to CreateOrUpdatePeerByUrlInput

* feat: update peer on duplicate request

* feat: update auto-peering resolver

* Update mutation

---------

Co-authored-by: Blair Currey <[email protected]>
Co-authored-by: Radu-Cristian Popa <[email protected]>
  • Loading branch information
3 people authored Sep 20, 2023
1 parent 43d715a commit f6936c4
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 26 deletions.
10 changes: 6 additions & 4 deletions packages/backend/src/accounting/psql/ledger-account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ export async function createAccount(

export async function getLiquidityAccount(
deps: ServiceDependencies,
accountRef: string
accountRef: string,
trx?: TransactionOrKnex
): Promise<LedgerAccount | undefined> {
return LedgerAccount.query(deps.knex)
return LedgerAccount.query(trx ?? deps.knex)
.findOne({ accountRef })
.whereNot({ type: LedgerAccountType.SETTLEMENT })
}

export async function getSettlementAccount(
deps: ServiceDependencies,
accountRef: string
accountRef: string,
trx?: TransactionOrKnex
): Promise<LedgerAccount | undefined> {
return LedgerAccount.query(deps.knex)
return LedgerAccount.query(trx ?? deps.knex)
.findOne({ accountRef })
.where({ type: LedgerAccountType.SETTLEMENT })
}
20 changes: 11 additions & 9 deletions packages/backend/src/accounting/psql/ledger-transfer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,25 +151,27 @@ function validateTransferStateUpdate(

export async function createTransfers(
deps: ServiceDependencies,
transfers: CreateLedgerTransferArgs[]
transfers: CreateLedgerTransferArgs[],
trx?: TransactionOrKnex
): Promise<CreateTransfersResult> {
const trx = await deps.knex.transaction()
const transaction =
(await trx?.transaction()) ?? (await deps.knex.transaction())

const errors: CreateTransferError[] = []
const results: LedgerTransfer[] = []

for (const [index, transfer] of transfers.entries()) {
const error = await validateTransfer(deps, transfer, trx)
const error = await validateTransfer(deps, transfer, transaction)

if (error) {
errors.push({ index, error })
break
}

try {
const createdTransfer = await LedgerTransfer.query(trx).insertAndFetch(
prepareTransfer(transfer)
)
const createdTransfer = await LedgerTransfer.query(
transaction
).insertAndFetch(prepareTransfer(transfer))

results.push(createdTransfer)
} catch (error) {
Expand All @@ -183,16 +185,16 @@ export async function createTransfers(
}

if (errors.length > 0) {
await trx.rollback()
await transaction.rollback()
return { results: [], errors }
}

try {
await trx.commit()
await transaction.commit()

return { results, errors: [] }
} catch (error) {
await trx.rollback()
await transaction.rollback()

const errorMessage = 'Could not create transfer(s)'
deps.logger.error(
Expand Down
11 changes: 6 additions & 5 deletions packages/backend/src/accounting/psql/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function createAccountingService(
getAccountsTotalReceived(deps, accountRefs),
getSettlementBalance: (ledger) => getSettlementBalance(deps, ledger),
createTransfer: (options) => createTransfer(deps, options),
createDeposit: (transfer) => createAccountDeposit(deps, transfer),
createDeposit: (transfer, trx) => createAccountDeposit(deps, transfer, trx),
createWithdrawal: (transfer) => createAccountWithdrawal(deps, transfer),
postWithdrawal: (withdrawalRef) => postTransfers(deps, [withdrawalRef]),
voidWithdrawal: (withdrawalRef) => voidTransfers(deps, [withdrawalRef])
Expand Down Expand Up @@ -290,7 +290,8 @@ function handleTransferCreateResults(

async function createAccountDeposit(
deps: ServiceDependencies,
args: Deposit
args: Deposit,
trx?: TransactionOrKnex
): Promise<void | TransferError> {
const {
id: transferRef,
Expand All @@ -302,8 +303,8 @@ async function createAccountDeposit(
} = args

const [account, settlementAccount] = await Promise.all([
getLiquidityAccount(deps, accountRef),
getSettlementAccount(deps, assetRef)
getLiquidityAccount(deps, accountRef, trx),
getSettlementAccount(deps, assetRef, trx)
])

if (!account) {
Expand All @@ -322,7 +323,7 @@ async function createAccountDeposit(
type: LedgerTransferType.DEPOSIT
}

const { errors } = await createTransfers(deps, [transfer])
const { errors } = await createTransfers(deps, [transfer], trx)

if (errors[0]) {
return errors[0].error
Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/accounting/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ export interface AccountingService {
getAccountsTotalReceived(ids: string[]): Promise<(bigint | undefined)[]>
getSettlementBalance(ledger: number): Promise<bigint | undefined>
createTransfer(options: TransferOptions): Promise<Transaction | TransferError>
createDeposit(deposit: Deposit): Promise<void | TransferError>
createDeposit(
deposit: Deposit,
trx?: TransactionOrKnex
): Promise<void | TransferError>
createWithdrawal(withdrawal: Withdrawal): Promise<void | TransferError>
postWithdrawal(id: string): Promise<void | TransferError>
voidWithdrawal(id: string): Promise<void | TransferError>
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/auto-peering/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('Auto Peering Service', (): void => {
assetId: asset.id,
maxPacketAmount: BigInt(args.maxPacketAmount!),
name: args.name,
initialLiquidity: BigInt(Number.MAX_SAFE_INTEGER),
http: {
incoming: { authTokens: [args.httpToken] },
outgoing: {
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/src/auto-peering/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ async function acceptPeeringRequest(
endpoint: args.ilpConnectorAddress,
authToken: outgoingHttpToken
}
}
},
initialLiquidity: BigInt(Number.MAX_SAFE_INTEGER)
})

const isDuplicatePeeringRequest =
Expand Down
9 changes: 6 additions & 3 deletions packages/backend/src/peer/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ export enum PeerError {
InvalidStaticIlpAddress = 'InvalidStaticIlpAddress',
InvalidHTTPEndpoint = 'InvalidHTTPEndpoint',
UnknownAsset = 'UnknownAsset',
UnknownPeer = 'UnknownPeer'
UnknownPeer = 'UnknownPeer',
InvalidInitialLiquidity = 'InvalidInitialLiquidity'
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
Expand All @@ -19,7 +20,8 @@ export const errorToCode: {
[PeerError.InvalidStaticIlpAddress]: 400,
[PeerError.InvalidHTTPEndpoint]: 400,
[PeerError.UnknownAsset]: 400,
[PeerError.UnknownPeer]: 404
[PeerError.UnknownPeer]: 404,
[PeerError.InvalidInitialLiquidity]: 400
}

export const errorToMessage: {
Expand All @@ -31,5 +33,6 @@ export const errorToMessage: {
[PeerError.InvalidStaticIlpAddress]: 'invalid ILP address',
[PeerError.InvalidHTTPEndpoint]: 'invalid HTTP endpoint',
[PeerError.UnknownAsset]: 'unknown asset',
[PeerError.UnknownPeer]: 'unknown peer'
[PeerError.UnknownPeer]: 'unknown peer',
[PeerError.InvalidInitialLiquidity]: 'invalid initial liquidity for peer'
}
28 changes: 26 additions & 2 deletions packages/backend/src/peer/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ import { getPageTests } from '../shared/baseModel.test'
import { createAsset } from '../tests/asset'
import { createPeer } from '../tests/peer'
import { truncateTables } from '../tests/tableManager'
import { AccountingService } from '../accounting/service'

describe('Peer Service', (): void => {
let deps: IocContract<AppServices>
let appContainer: TestContainer
let peerService: PeerService
let accountingService: AccountingService
let asset: Asset

const randomPeer = (): CreateOptions => ({
const randomPeer = (override?: Partial<CreateOptions>): CreateOptions => ({
assetId: asset.id,
http: {
incoming: {
Expand All @@ -36,13 +38,15 @@ describe('Peer Service', (): void => {
maxPacketAmount: BigInt(100),
staticIlpAddress: 'test.' + uuid(),
name: faker.person.fullName(),
liquidityThreshold: BigInt(10000)
liquidityThreshold: BigInt(10000),
...override
})

beforeAll(async (): Promise<void> => {
deps = await initIocContainer(Config)
appContainer = await createTestApp(deps)
peerService = await deps.use('peerService')
accountingService = await deps.use('accountingService')
})

beforeEach(async (): Promise<void> => {
Expand Down Expand Up @@ -122,6 +126,26 @@ describe('Peer Service', (): void => {
)
})

test('Can create peer with an initial amount of liquidity', async (): Promise<void> => {
const initialLiquidity = 100n
const options = randomPeer({ initialLiquidity })
const peer = await peerService.create(options)
assert.ok(!isPeerError(peer))

await expect(accountingService.getBalance(peer.id)).resolves.toBe(
initialLiquidity
)
})

test('Cannot create peer with invalid initial liquidity', async (): Promise<void> => {
const initialLiquidity = -100n
const options = randomPeer({ initialLiquidity })

await expect(peerService.create(options)).resolves.toEqual(
PeerError.InvalidInitialLiquidity
)
})

test('Cannot fetch a bogus peer', async (): Promise<void> => {
await expect(peerService.get(uuid())).resolves.toBeUndefined()
})
Expand Down
40 changes: 39 additions & 1 deletion packages/backend/src/peer/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { HttpTokenError } from '../httpToken/errors'
import { Pagination } from '../shared/baseModel'
import { BaseService } from '../shared/baseService'
import { isValidHttpUrl } from '../shared/utils'
import { v4 as uuid } from 'uuid'
import { TransferError } from '../accounting/errors'

export interface HttpOptions {
incoming?: {
Expand All @@ -34,6 +36,7 @@ export type Options = {
staticIlpAddress: string
name?: string
liquidityThreshold?: bigint
initialLiquidity?: bigint
}

export type CreateOptions = Options & {
Expand Down Expand Up @@ -142,9 +145,27 @@ async function createPeer(
id: peer.id,
asset: peer.asset
},
LiquidityAccountType.PEER
LiquidityAccountType.PEER,
trx
)

if (options.initialLiquidity) {
const transferError = await addLiquidity(
deps,
{ peer, amount: options.initialLiquidity },
trx
)

if (transferError) {
deps.logger.error(
{ transferError },
'error trying to add initial liquidity'
)

throw PeerError.InvalidInitialLiquidity
}
}

return peer
})
} catch (err) {
Expand Down Expand Up @@ -212,6 +233,23 @@ async function updatePeer(
}
}

async function addLiquidity(
deps: ServiceDependencies,
args: { peer: Peer; amount: bigint; transferId?: string },
trx?: TransactionOrKnex
): Promise<void | TransferError> {
const { peer, amount, transferId } = args

return deps.accountingService.createDeposit(
{
id: transferId ?? uuid(),
account: peer,
amount
},
trx
)
}

async function addIncomingHttpTokens({
deps,
peerId,
Expand Down

0 comments on commit f6936c4

Please sign in to comment.