diff --git a/apps/whale-api/docker-compose.yml b/apps/whale-api/docker-compose.yml index 20d3a2456f..95e65e3e85 100644 --- a/apps/whale-api/docker-compose.yml +++ b/apps/whale-api/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: defi-blockchain: - image: defi/defichain:4.0.0 + image: defi/defichain:4.0.5 ports: - "19554:19554" diff --git a/docs/node/CATEGORIES/08-account.md b/docs/node/CATEGORIES/08-account.md index 29104d1a5e..16390bfa9f 100644 --- a/docs/node/CATEGORIES/08-account.md +++ b/docs/node/CATEGORIES/08-account.md @@ -159,7 +159,12 @@ Create a transfer domain transaction submitted to a connected node. ```ts title="client.account.transferDomain()" interface account { - transferDomain (payload: Array>): Promise + transferDomain (payload: Array | Record>): Promise +} + +enum TransferDomainKey { + SRC = 'src', + DST = 'dst' } interface TransferDomainInfo { @@ -176,7 +181,15 @@ enum TransferDomainType { DVM = 2, /** type for EVM To DVM Token transfer */ EVM = 3, -}; +} + +enum TransferDomainOptionalKey { + SINGLE_KEY_CHECK = 'singlekeycheck', +} + +interface TransferDomainOptionalInfo { + singleKeyCheck: boolean +} ``` ## accountToUtxos diff --git a/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts b/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts index bca3785e0b..81094c79c2 100644 --- a/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts @@ -1,6 +1,6 @@ import { MasterNodeRegTestContainer } from '@defichain/testcontainers' import { ContainerAdapterClient } from '../../container_adapter_client' -import { TransferDomainType } from '../../../src/category/account' +import { TransferDomainType, TransferDomainOptionalInfo } from '../../../src/category/account' import waitForExpect from 'wait-for-expect' import BigNumber from 'bignumber.js' @@ -137,7 +137,8 @@ describe('Account', () => { address: evmAddr, amount: '3@DFI', domain: TransferDomainType.EVM - } + }, + singlekeycheck: false as unknown as TransferDomainOptionalInfo } ]) await container.generate(1) diff --git a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.singlekeycheck.test.ts b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.singlekeycheck.test.ts new file mode 100644 index 0000000000..9bf3741747 --- /dev/null +++ b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.singlekeycheck.test.ts @@ -0,0 +1,136 @@ +import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { ContainerAdapterClient } from '../../container_adapter_client' +import { TransferDomainOptionalInfo, TransferDomainType } from '../../../src/category/account' +import { RpcApiError } from '@defichain/jellyfish-api-core' +import BigNumber from 'bignumber.js' + +describe('TransferDomain', () => { + let legacyAddr: string + let legacyEvmAddr: string + let bech32Addr: string + let bech32EvmAddr: string + const container = new MasterNodeRegTestContainer() + const client = new ContainerAdapterClient(container) + + beforeAll(async () => { + await container.start() + await container.waitForWalletCoinbaseMaturity() + + await client.masternode.setGov({ + ATTRIBUTES: { + 'v0/params/feature/evm': 'true', + 'v0/params/feature/transferdomain': 'true' + } + }) + await container.generate(2) + + legacyAddr = await container.getNewAddress('legacy', 'legacy') + legacyEvmAddr = (await container.call('addressmap', [legacyAddr, 1])).format.erc55 + + bech32Addr = await container.getNewAddress('bech32', 'bech32') + bech32EvmAddr = (await container.call('addressmap', [bech32Addr, 1])).format.erc55 + + await container.call('utxostoaccount', [{ [legacyAddr]: '100000@0' }]) + await container.generate(1) + }) + + afterAll(async () => { + await container.stop() + }) + + describe('single key check restricted', () => { + it('should fail as src key and dst key is different', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: legacyAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: bech32EvmAddr, + amount: '3@DFI', + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Dst address does not match source key') + }) + + it('should transfer domain with same key', async () => { + const legacyAccBefore = await client.account.getAccount(legacyAddr) + const [value] = legacyAccBefore[0].split('@') + const legacyBalanceBefore = new BigNumber(value) + const evmBalanceBefore = await getEVMBalances(client) + + await client.account.transferDomain([ + { + src: { + address: legacyAddr, + amount: '1@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: legacyEvmAddr, + amount: '1@DFI', + domain: TransferDomainType.EVM + } + } + ]) + await container.generate(1) + + { + const legacyAccAfter = await client.account.getAccount(legacyAddr) + const [value] = legacyAccAfter[0].split('@') + const legacyBalanceAfter = new BigNumber(value) + expect(legacyBalanceAfter).toStrictEqual(legacyBalanceBefore.minus(1)) + + const evmBalanceAfter = await getEVMBalances(client) + expect(evmBalanceAfter).toStrictEqual(evmBalanceBefore.plus(1)) + } + }) + + it('should transfer domain with different key', async () => { + const legacyAccBefore = await client.account.getAccount(legacyAddr) + const [value] = legacyAccBefore[0].split('@') + const legacyBalanceBefore = new BigNumber(value) + const evmBalanceBefore = await getEVMBalances(client) + + await client.account.transferDomain([ + { + src: { + address: legacyAddr, + amount: '2@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: bech32EvmAddr, + amount: '2@DFI', + domain: TransferDomainType.EVM + }, + singlekeycheck: false as unknown as TransferDomainOptionalInfo + } + ]) + await container.generate(1) + + { + const legacyAccAfter = await client.account.getAccount(legacyAddr) + const [value] = legacyAccAfter[0].split('@') + const legacyBalanceAfter = new BigNumber(value) + expect(legacyBalanceAfter).toStrictEqual(legacyBalanceBefore.minus(2)) + + const evmBalanceAfter = await getEVMBalances(client) + expect(evmBalanceAfter).toStrictEqual(evmBalanceBefore.plus(2)) + } + }) + }) +}) + +async function getEVMBalances (client: ContainerAdapterClient): Promise { + const ethRes = await client.account.getTokenBalances({}, false) + const [eth] = ethRes[0].split('@') + const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) + const [withEth] = withEthRes[0].split('@') + return new BigNumber(withEth).minus(eth) +} diff --git a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts index 963ad0135d..108c05ff2c 100644 --- a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts @@ -1,4 +1,4 @@ -import { MasterNodeRegTestContainer } from '@defichain/testcontainers' +import { MasterNodeRegTestContainer, StartFlags } from '@defichain/testcontainers' import { ContainerAdapterClient } from '../../container_adapter_client' import { TransferDomainType } from '../../../src/category/account' import { RpcApiError } from '@defichain/jellyfish-api-core' @@ -8,9 +8,11 @@ describe('TransferDomain', () => { let dvmAddr: string, evmAddr: string, p2shAddr: string const container = new MasterNodeRegTestContainer() const client = new ContainerAdapterClient(container) + // add `-tdsinglekeycheck` for backward compatibility + const startFlags: StartFlags[] = [{ name: 'tdsinglekeycheck', value: 0 }] beforeAll(async () => { - await container.start() + await container.start({ startFlags }) await container.waitForWalletCoinbaseMaturity() await client.masternode.setGov({ diff --git a/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts b/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts index 0a76dbf6a8..21cf11edb5 100644 --- a/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts @@ -3,7 +3,7 @@ import { MasterNodeRegTestContainer } from '@defichain/testcontainers' import { Testing } from '@defichain/jellyfish-testing' import { RpcApiError } from '@defichain/jellyfish-api-core/dist/index' import { ContainerAdapterClient } from '../../container_adapter_client' -import { TransferDomainType } from '../../../src/category/account' +import { TransferDomainType, TransferDomainOptionalInfo } from '../../../src/category/account' import BigNumber from 'bignumber.js' describe('EVMTX', () => { @@ -69,7 +69,8 @@ describe('EVMTX', () => { address: ethAddress, amount: `${amount.HUNDRED}@DFI`, domain: TransferDomainType.EVM - } + }, + singlekeycheck: false as unknown as TransferDomainOptionalInfo } ] await container.call('transferdomain', [dvmToEvmTransfer]) diff --git a/packages/jellyfish-api-core/src/category/account.ts b/packages/jellyfish-api-core/src/category/account.ts index 8576f845df..1bb7ed0480 100644 --- a/packages/jellyfish-api-core/src/category/account.ts +++ b/packages/jellyfish-api-core/src/category/account.ts @@ -66,6 +66,10 @@ export enum TransferDomainKey { DST = 'dst' } +export enum TransferDomainOptionalKey { + SINGLE_KEY_CHECK = 'singlekeycheck', +} + /** * Account RPCs for DeFi Blockchain */ @@ -296,16 +300,18 @@ export class Account { /** * Create an transfer domain transaction submitted to a connected node. * - * @param {Array>} payload[] - * @param {Record} payload + * @param {Array | Record>} payload[] + * @param {Record | Record} payload * @param {TransferDomainInfo} info * @param {string} info.address * @param {string} info.amount * @param {TransferDomainType} info.domain * @param {string} [info.data] optional data, note: currently its not used + * @param {TransferDomainOptionalInfo} optionalInfo + * @param {boolean} [optionalInfo.singleKeyCheck=true] * @return {Promise} */ - async transferDomain (payload: Array>): Promise { + async transferDomain (payload: Array | Record>): Promise { return await this.client.call('transferdomain', [payload], 'number') } @@ -725,3 +731,7 @@ export interface TransferDomainInfo { amount: string domain: TransferDomainType } + +export interface TransferDomainOptionalInfo { + singleKeyCheck: boolean +} diff --git a/packages/testcontainers/src/containers/DeFiDContainer.ts b/packages/testcontainers/src/containers/DeFiDContainer.ts index e24f14f739..810e1352d6 100644 --- a/packages/testcontainers/src/containers/DeFiDContainer.ts +++ b/packages/testcontainers/src/containers/DeFiDContainer.ts @@ -36,7 +36,7 @@ export abstract class DeFiDContainer extends DockerContainer { if (process?.env?.DEFICHAIN_DOCKER_IMAGE !== undefined) { return process.env.DEFICHAIN_DOCKER_IMAGE } - return 'defi/defichain:4.0.0' // renovate.json regexManagers + return 'defi/defichain:4.0.5' // renovate.json regexManagers } public static readonly DefaultStartOptions = { diff --git a/packages/testcontainers/src/containers/NativeChainContainer.ts b/packages/testcontainers/src/containers/NativeChainContainer.ts index dcaad5ccd7..113fce02d7 100644 --- a/packages/testcontainers/src/containers/NativeChainContainer.ts +++ b/packages/testcontainers/src/containers/NativeChainContainer.ts @@ -29,7 +29,7 @@ export class NativeChainContainer extends GenericContainer { if (process?.env?.DEFICHAIN_DOCKER_IMAGE !== undefined) { return process.env.DEFICHAIN_DOCKER_IMAGE } - return 'defi/defichain:4.0.0' // renovate.json regexManagers + return 'defi/defichain:4.0.5' // renovate.json regexManagers } public static readonly PREFIX = 'defichain-testcontainers-'