Skip to content

Commit

Permalink
chore(backend): only call onCredit for destinationAccount
Browse files Browse the repository at this point in the history
Remove onDebit.
Remove TransferAccount.
Update invoices index.
Update transaction-api.md docs.
  • Loading branch information
wilsonianb committed Feb 9, 2022
1 parent e47695c commit 07661cf
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 67 deletions.
21 changes: 10 additions & 11 deletions docs/transaction-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ An expired invoice that has never received money is deleted.

Rafiki sends webhook events to notify the wallet of payment lifecycle states that require liquidity to be added or removed.

Webhook event handlers must be idempotent and return `200`.
Webhook event handlers must be idempotent and return `200` on success. Rafiki will retry unsuccessful webhook requests for up to one day.

### `EventType`

Expand Down Expand Up @@ -185,13 +185,12 @@ The intent must include `invoiceUrl` xor (`paymentPointer` and `amountToSend`).

### `Invoice`

| Name | Optional | Type | Description |
| :------------ | :------- | :-------- | :----------------------------------------------------------------------------------------------------------------------------- |
| `id` | No | `ID` | Unique ID for this invoice, randomly generated by Rafiki. |
| `accountId` | No | `String` | Id of the recipient's Open Payments account. |
| `amount` | No | `UInt64` | The amount that must be paid at the time the invoice is created, in base units of the account asset. |
| `received` | No | `UInt64` | The total amount received, in base units of the account asset. |
| `active` | No | `Boolean` | If `true`, the invoice may receive funds. If `false`, the invoice is either expired or has already received `amount` of funds. |
| `description` | Yes | `String` | Human readable description of the invoice. |
| `createdAt` | No | `String` | |
| `expiresAt` | No | `String` | |
| Name | Optional | Type | Description |
| :------------ | :------- | :------- | :--------------------------------------------------------------------------------------------------- |
| `id` | No | `ID` | Unique ID for this invoice, randomly generated by Rafiki. |
| `accountId` | No | `String` | Id of the recipient's Open Payments account. |
| `amount` | No | `UInt64` | The amount that must be paid at the time the invoice is created, in base units of the account asset. |
| `received` | No | `UInt64` | The total amount received, in base units of the account asset. |
| `description` | Yes | `String` | Human readable description of the invoice. |
| `createdAt` | No | `String` | |
| `expiresAt` | No | `String` | |
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ exports.up = function (knex) {

table.index(['accountId', 'createdAt', 'id'])

table.index('expiresAt')
table.index(['active', 'expiresAt'])
/*
TODO: The latest version of knex supports "partial indexes", which would be more efficient for the deactivateInvoice use case. Unfortunately, the only version of 'objection' that supports this version of knex is still in alpha.
// This is a 'partial index' -- expiresAt is only indexed when active=true.
Expand Down
7 changes: 3 additions & 4 deletions packages/backend/src/accounting/service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { v4 as uuid } from 'uuid'
import {
AccountingService,
LiquidityAccount,
TransferAccount,
Deposit,
Withdrawal
} from './service'
Expand All @@ -25,7 +24,7 @@ import {
startTigerbeetleContainer,
TIGERBEETLE_PORT
} from '../tests/tigerbeetle'
import { AccountFactory } from '../tests/accountFactory'
import { AccountFactory, FactoryAccount } from '../tests/accountFactory'

describe('Accounting Service', (): void => {
let deps: IocContract<AppServices>
Expand Down Expand Up @@ -215,8 +214,8 @@ describe('Accounting Service', (): void => {
${true} | ${'same asset'}
${false} | ${'cross-currency'}
`('$description', ({ sameAsset }): void => {
let sourceAccount: TransferAccount
let destinationAccount: TransferAccount
let sourceAccount: LiquidityAccount
let destinationAccount: FactoryAccount
const startingSourceBalance = BigInt(10)
const startingDestinationLiquidity = BigInt(100)

Expand Down
64 changes: 22 additions & 42 deletions packages/backend/src/accounting/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ export interface LiquidityAccount {
unit: number
}
onCredit?: (balance: bigint) => Promise<LiquidityAccount>
onDebit?: (balance: bigint) => Promise<LiquidityAccount>
}

export interface Deposit {
Expand All @@ -58,15 +57,9 @@ export interface Withdrawal extends Deposit {
timeout: bigint
}

export interface TransferAccount extends LiquidityAccount {
asset: LiquidityAccount['asset'] & {
asset: LiquidityAccount['asset']
}
}

export interface TransferOptions {
sourceAccount: TransferAccount
destinationAccount: TransferAccount
sourceAccount: LiquidityAccount
destinationAccount: LiquidityAccount
sourceAmount: bigint
destinationAmount?: bigint
timeout: bigint // nano-seconds
Expand Down Expand Up @@ -227,34 +220,30 @@ export async function createTransfer(
return TransferError.InvalidDestinationAmount
}
const transfers: Required<CreateTransferOptions>[] = []
const sourceAccounts: LiquidityAccount[] = []
const destinationAccounts: LiquidityAccount[] = []

const addTransfer = ({
sourceAccount,
destinationAccount,
sourceAccountId,
destinationAccountId,
amount
}: {
sourceAccount: LiquidityAccount
destinationAccount: LiquidityAccount
sourceAccountId: string
destinationAccountId: string
amount: bigint
}) => {
transfers.push({
id: uuid(),
sourceAccountId: sourceAccount.id,
destinationAccountId: destinationAccount.id,
sourceAccountId,
destinationAccountId,
amount,
timeout
})
sourceAccounts.push(sourceAccount)
destinationAccounts.push(destinationAccount)
}

// Same asset
if (sourceAccount.asset.unit === destinationAccount.asset.unit) {
addTransfer({
sourceAccount,
destinationAccount,
sourceAccountId: sourceAccount.id,
destinationAccountId: destinationAccount.id,
amount:
destinationAmount && destinationAmount < sourceAmount
? destinationAmount
Expand All @@ -265,15 +254,15 @@ export async function createTransfer(
// Send excess source amount to liquidity account
if (destinationAmount < sourceAmount) {
addTransfer({
sourceAccount,
destinationAccount: sourceAccount.asset,
sourceAccountId: sourceAccount.id,
destinationAccountId: sourceAccount.asset.id,
amount: sourceAmount - destinationAmount
})
// Deliver excess destination amount from liquidity account
} else {
addTransfer({
sourceAccount: destinationAccount.asset,
destinationAccount,
sourceAccountId: destinationAccount.asset.id,
destinationAccountId: destinationAccount.id,
amount: destinationAmount - sourceAmount
})
}
Expand All @@ -287,13 +276,13 @@ export async function createTransfer(
// Send to source liquidity account
// Deliver from destination liquidity account
addTransfer({
sourceAccount,
destinationAccount: sourceAccount.asset,
sourceAccountId: sourceAccount.id,
destinationAccountId: sourceAccount.asset.id,
amount: sourceAmount
})
addTransfer({
sourceAccount: destinationAccount.asset,
destinationAccount,
sourceAccountId: destinationAccount.asset.id,
destinationAccountId: destinationAccount.id,
amount: destinationAmount
})
}
Expand Down Expand Up @@ -327,19 +316,10 @@ export async function createTransfer(
if (error) {
return error.error
}
for (const account of sourceAccounts) {
if (account.onDebit) {
const balance = await getAccountBalance(deps, account.id)
assert.ok(balance !== undefined)
await account.onDebit(balance)
}
}
for (const account of destinationAccounts) {
if (account.onCredit) {
const balance = await getAccountBalance(deps, account.id)
assert.ok(balance !== undefined)
await account.onCredit(balance)
}
if (destinationAccount.onCredit) {
const balance = await getAccountBalance(deps, destinationAccount.id)
assert.ok(balance !== undefined)
await destinationAccount.onCredit(balance)
}
},
rollback: async (): Promise<void | TransferError> => {
Expand Down
6 changes: 3 additions & 3 deletions packages/backend/src/connector/core/rafiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
import { createTokenAuthMiddleware } from './middleware'
import { RatesService } from '../../rates/service'
import { TransferError } from '../../accounting/errors'
import { TransferAccount, Transaction } from '../../accounting/service'
import { LiquidityAccount, Transaction } from '../../accounting/service'
import { AssetOptions } from '../../asset/service'
import { AccountService } from '../../open_payments/account/service'
import { InvoiceService } from '../../open_payments/invoice/service'
Expand All @@ -27,8 +27,8 @@ import { PeerService } from '../../peer/service'
// ../../open_payments/invoice/model
// ../../outgoing_payment/model
// ../../peer/model
export interface ConnectorAccount extends TransferAccount {
asset: TransferAccount['asset'] & AssetOptions
export interface ConnectorAccount extends LiquidityAccount {
asset: LiquidityAccount['asset'] & AssetOptions
}

export interface IncomingAccount extends ConnectorAccount {
Expand Down
19 changes: 13 additions & 6 deletions packages/backend/src/tests/accountFactory.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import { v4 as uuid } from 'uuid'

import {
AccountingService,
LiquidityAccount,
TransferAccount
} from '../accounting/service'
import { AccountingService, LiquidityAccount } from '../accounting/service'
import { randomUnit } from './asset'

type BuildOptions = Partial<LiquidityAccount> & {
balance?: bigint
}

export type FactoryAccount = Omit<LiquidityAccount, 'asset'> & {
asset: {
id: string
unit: number
asset: {
id: string
unit: number
}
}
}

export class AccountFactory {
public constructor(
private accounts: AccountingService,
private unitGenerator: () => number = randomUnit
) {}

public async build(options: BuildOptions = {}): Promise<TransferAccount> {
public async build(options: BuildOptions = {}): Promise<FactoryAccount> {
const assetId = options.asset?.id || uuid()
const unit = options.asset?.unit || this.unitGenerator()
const asset = {
Expand Down

0 comments on commit 07661cf

Please sign in to comment.