Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add accountToAccount RPC #284

Merged
merged 23 commits into from
May 28, 2021
Merged
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
17 changes: 13 additions & 4 deletions packages/jellyfish-api-core/__tests__/category/account.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../container_adapter_client'
import waitForExpect from 'wait-for-expect'
import BigNumber from 'bignumber.js'
import { UtxosToAccountPayload, AccountHistoryCountOptions, TxType } from '../../src/category/account'
import { RpcApiError } from '../../src'
import { BalanceTransferPayload, UTXO, AccountHistoryCountOptions, TxType } from '../../src/category/account'

describe('masternode', () => {
const container = new MasterNodeRegTestContainer()
Expand Down Expand Up @@ -485,7 +486,7 @@ describe('masternode', () => {

describe('utxosToAccount', () => {
it('should utxosToAccount', async () => {
const payload: UtxosToAccountPayload = {}
const payload: BalanceTransferPayload = {}
// NOTE(jingyi2811): Only support sending utxos to DFI account.
payload[await container.getNewAddress()] = '5@DFI'
payload[await container.getNewAddress()] = '5@DFI'
Expand All @@ -496,14 +497,22 @@ describe('masternode', () => {
expect(data.length).toStrictEqual(64)
})

it('should not utxosToAccount for DFI coin if does not own the recipient address', async () => {
// NOTE(jingyi2811): Only support sending utxos to DFI account.
const promise = client.account.utxosToAccount({ '2Mywjs9zEU4NtLknXQJZgozaxMvPn2Bb3qz': '5@DFI' })

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('The address (2Mywjs9zEU4NtLknXQJZgozaxMvPn2Bb3qz) is not your own address')
})

it('should utxosToAccount with utxos', async () => {
const payload: UtxosToAccountPayload = {}
const payload: BalanceTransferPayload = {}
// NOTE(jingyi2811): Only support sending utxos to DFI account.
payload[await container.getNewAddress()] = '5@DFI'
payload[await container.getNewAddress()] = '5@DFI'

const utxos = await container.call('listunspent')
const inputs = utxos.map((utxo: { txid: string, vout: number }) => {
const inputs: UTXO[] = utxos.map((utxo: UTXO) => {
return {
txid: utxo.txid,
vout: utxo.vout
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { MasterNodeRegTestContainer } from '@defichain/testcontainers'
import { ContainerAdapterClient } from '../../container_adapter_client'
import { RpcApiError } from '../../../src'
import { BalanceTransferPayload, UTXO } from '../../../src/category/account'

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

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

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

let from: string

async function setup (): Promise<void> {
from = await container.call('getnewaddress')
await createToken(from, 'DBTC', 200)

const to = await accountToAccount('DBTC', 5, from)
await accountToAccount('DBTC', 18, from, to)

await createToken(from, 'DETH', 200)
await accountToAccount('DETH', 46, from)
}

async function createToken (address: string, symbol: string, amount: number): Promise<void> {
const metadata = {
symbol,
name: symbol,
isDAT: true,
mintable: true,
tradeable: true,
collateralAddress: address
}
await container.waitForWalletBalanceGTE(101)
await container.call('createtoken', [metadata])
await container.generate(1)

await container.call('utxostoaccount', [{ [address]: '100@0' }])
await container.generate(1)

await container.call('minttokens', [`${amount.toString()}@${symbol}`])
await container.generate(1)
}

async function accountToAccount (symbol: string, amount: number, from: string, _to = ''): Promise<string> {
const to = _to !== '' ? _to : await container.call('getnewaddress')

await container.call('accounttoaccount', [from, { [to]: `${amount.toString()}@${symbol}` }])
await container.generate(1)

return to
}

describe('accountToAccount', () => {
it('should accountToAccount', async () => {
const payload: BalanceTransferPayload = {}
payload[await container.getNewAddress()] = '5@DFI'
payload[await container.getNewAddress()] = '5@DBTC'
payload[await container.getNewAddress()] = '5@DETH'

const data = await client.account.accountToAccount(from, payload)

expect(typeof data).toStrictEqual('string')
expect(data.length).toStrictEqual(64)
})

it('should not accountToAccount for DFI coin if does not own the recipient address', async () => {
const promise = client.account.accountToAccount(from, { '2Mywjs9zEU4NtLknXQJZgozaxMvPn2Bb3qz': '5@DFI' })

await expect(promise).rejects.toThrow(RpcApiError)
await expect(promise).rejects.toThrow('The address (2Mywjs9zEU4NtLknXQJZgozaxMvPn2Bb3qz) is not your own address')
})

it('should accountToAccount with utxos', async () => {
const { txid } = await container.fundAddress(from, 10)

const payload: BalanceTransferPayload = {}
payload[await container.getNewAddress()] = '5@DFI'
payload[await container.getNewAddress()] = '5@DBTC'
payload[await container.getNewAddress()] = '5@DETH'

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

const data = await client.account.accountToAccount(from, payload, { utxos: inputs })

expect(typeof data).toStrictEqual('string')
expect(data.length).toStrictEqual(64)
})
})
})
38 changes: 31 additions & 7 deletions packages/jellyfish-api-core/src/category/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export enum TxType {
SET_GOV_VARIABLE = 'G',
AUTO_AUTH_PREP = 'A'
}

type AccountRegexType = `${string}@${string}`

/**
* Account RPCs for DeFi Blockchain
*/
Expand Down Expand Up @@ -244,20 +247,37 @@ export class Account {
}

/**
* Creates and submits to a connect node; a transfer transaction from the wallet UTXOs to a specified account.
* Create an UTXOs to Account transaction submitted to a connected node.
* Optionally, specific UTXOs to spend to create that transaction.
*
* @param {UtxosToAccountPayload} payload
* @param {BalanceTransferPayload} payload
* @param {string} payload[address]
* @param {UtxosToAccountUTXO[]} [utxos=[]]
* @param {UTXO[]} [utxos = []]
* @param {string} [utxos.txid]
* @param {number} [utxos.vout]
* @return {Promise<string>}
*/
async utxosToAccount (payload: UtxosToAccountPayload, utxos: UtxosToAccountUTXO[] = []): Promise<string> {
async utxosToAccount (payload: BalanceTransferPayload, utxos: UTXO[] = []): Promise<string> {
return await this.client.call('utxostoaccount', [payload, utxos], 'number')
}

/**
* Create an Account to Account transaction submitted to a connected node.
* Optionally, specific UTXOs to spend to create that transaction.
*
* @param {string} from
* @param {BalanceTransferPayload} payload
* @param {string} payload[address]
* @param {AccountToAccountOptions} [options]
* @param {UTXO[]} [options.utxos = []]
* @param {string} [options.utxos.txid]
* @param {number} [options.utxos.vout]
* @return {Promise<string>}
*/
async accountToAccount (from: string, payload: BalanceTransferPayload, options: AccountToAccountOptions = { utxos: [] }): Promise<string> {
return await this.client.call('accounttoaccount', [from, payload, options.utxos], 'number')
}

/**
* Returns count of account history
*
Expand Down Expand Up @@ -332,11 +352,15 @@ export interface AccountHistoryOptions {
limit?: number
}

export interface UtxosToAccountPayload {
[key: string]: string
export interface BalanceTransferPayload {
[key: string]: AccountRegexType
}

export interface AccountToAccountOptions {
utxos?: UTXO[]
}

export interface UtxosToAccountUTXO {
export interface UTXO {
txid: string
vout: number
}
Expand Down
38 changes: 33 additions & 5 deletions website/docs/jellyfish/api/account.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,19 +148,47 @@ interface AccountHistoryOptions {

## utxosToAccount

Creates and submits to a connect node; a transfer transaction from the wallet UTXOs to a specified account.
Create an UTXOs to Account transaction submitted to a connected node.
Optionally, specific UTXOs to spend to create that transaction.

```ts title="client.account.utxosToAccount()"
interface account {
utxosToAccount (payload: UtxosToAccountPayload, utxos: UtxosToAccountUTXO[] = []): Promise<string>
utxosToAccount (payload: BalanceTransferPayload, utxos: UTXO[] = []): Promise<string>
}

interface UtxosToAccountPayload {
[key: string]: string;
type AccountRegexType = `${string}@${string}`

interface BalanceTransferPayload {
[key: string]: AccountRegexType
}

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

## accountToAccount

Create an Account to Account transaction submitted to a connected node.
Optionally, specific UTXOs to spend to create that transaction.

```ts title="client.account.accountToAccount()"
interface account {
accountToAccount (from: string, payload: BalanceTransferPayload, options: AccountToAccountOptions = { utxos: [] }): Promise<string>
}

type AccountRegexType = `${string}@${string}`

interface BalanceTransferPayload {
[key: string]: AccountRegexType
}

interface AccountToAccountOptions {
utxos?: UTXO[]
}

interface UtxosToAccountUTXO {
interface UTXO {
txid: string
vout: number
}
Expand Down