diff --git a/apps/playground-api/__tests__/modules/PlaygroundModule.test.ts b/apps/playground-api/__tests__/modules/PlaygroundModule.test.ts index 44cd39779..1330fe2c7 100644 --- a/apps/playground-api/__tests__/modules/PlaygroundModule.test.ts +++ b/apps/playground-api/__tests__/modules/PlaygroundModule.test.ts @@ -201,6 +201,9 @@ it('should have gov set', async () => { 'v0/token/18/loan_minting_interest': '3', 'v0/params/feature/icx': 'true', 'v0/params/feature/evm': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/params/foundation/members': [ 'bcrt1qyrfrpadwgw7p5eh3e9h3jmu4kwlz4prx73cqny', diff --git a/apps/playground-api/src/setups/setup.gov.ts b/apps/playground-api/src/setups/setup.gov.ts index a440fea95..b345ee761 100644 --- a/apps/playground-api/src/setups/setup.gov.ts +++ b/apps/playground-api/src/setups/setup.gov.ts @@ -158,6 +158,9 @@ export class SetupGov extends PlaygroundSetup> { 'v0/params/feature/evm': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/transferdomain/dvm-evm/enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', 'v0/transferdomain/dvm-evm/src-formats': ['p2pkh', 'bech32'], 'v0/transferdomain/dvm-evm/dest-formats': ['erc55'], 'v0/transferdomain/evm-dvm/src-formats': ['erc55'], diff --git a/apps/whale-api/docker-compose.yml b/apps/whale-api/docker-compose.yml index 0f5e0b0ec..e98c875b6 100644 --- a/apps/whale-api/docker-compose.yml +++ b/apps/whale-api/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.7' +version: "3.7" services: defi-blockchain: 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 9d2d52fe9..bca3785e0 100644 --- a/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/account/getTokenBalances.test.ts @@ -108,6 +108,9 @@ describe('Account', () => { 'v0/params/feature/evm': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/transferdomain/dvm-evm/enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', 'v0/transferdomain/dvm-evm/src-formats': ['p2pkh', 'bech32'], 'v0/transferdomain/dvm-evm/dest-formats': ['erc55'], 'v0/transferdomain/evm-dvm/src-formats': ['erc55'], @@ -117,7 +120,7 @@ describe('Account', () => { }) await container.generate(2) - const dvmAddr = await container.call('getnewaddress', ['', 'bech32']) + const dvmAddr = await container.getNewAddress('dvm', 'legacy') const evmAddr = await container.getNewAddress('erc55', 'erc55') await container.call('utxostoaccount', [{ [dvmAddr]: '100@0' }]) 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 02a74d6c6..5791b52a9 100644 --- a/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/account/transferDomain.test.ts @@ -1,11 +1,11 @@ import { MasterNodeRegTestContainer } from '@defichain/testcontainers' import { ContainerAdapterClient } from '../../container_adapter_client' import { TransferDomainType } from '../../../src/category/account' -import { RpcApiError } from '@defichain/jellyfish-api-core/dist/index' +import { RpcApiError } from '@defichain/jellyfish-api-core' import BigNumber from 'bignumber.js' describe('TransferDomain', () => { - let dvmAddr: string, evmAddr: string + let dvmAddr: string, evmAddr: string, p2shAddr: string const container = new MasterNodeRegTestContainer() const client = new ContainerAdapterClient(container) @@ -18,6 +18,9 @@ describe('TransferDomain', () => { 'v0/params/feature/evm': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/transferdomain/dvm-evm/enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', 'v0/transferdomain/dvm-evm/src-formats': ['p2pkh', 'bech32'], 'v0/transferdomain/dvm-evm/dest-formats': ['erc55'], 'v0/transferdomain/evm-dvm/src-formats': ['erc55'], @@ -27,10 +30,12 @@ describe('TransferDomain', () => { }) await container.generate(2) - dvmAddr = await container.call('getnewaddress', ['', 'bech32']) + dvmAddr = await container.getNewAddress('address1', 'legacy') evmAddr = await container.getNewAddress('erc55', 'erc55') - await container.call('utxostoaccount', [{ [dvmAddr]: '100@0' }]) + p2shAddr = await container.getNewAddress('address', 'p2sh-segwit') + + await container.call('utxostoaccount', [{ [dvmAddr]: '100000@0' }]) await container.generate(1) await container.call('createtoken', [{ @@ -41,10 +46,40 @@ describe('TransferDomain', () => { tradeable: true, collateralAddress: dvmAddr }]) + await container.call('createtoken', [{ + symbol: 'ETH', + name: 'ETH', + isDAT: true, + mintable: true, + tradeable: true, + collateralAddress: dvmAddr + }]) + await container.call('createtoken', [{ + name: 'DESC', + symbol: 'DESC', + isDAT: false, + mintable: true, + tradeable: true, + collateralAddress: dvmAddr + }]) + await container.generate(1) + + // Create LP tokens + const metadata = { + tokenA: 'DFI', + tokenB: 'BTC', + commission: 1, + status: true, + ownerAddress: dvmAddr + } + await client.poolpair.createPoolPair(metadata) await container.generate(1) await container.call('minttokens', ['10@BTC']) + await container.call('minttokens', ['10@ETH']) + await container.call('minttokens', ['10@DESC#128']) await container.generate(1) + await createLoanToken(container, dvmAddr) }) afterAll(async () => { @@ -52,70 +87,89 @@ describe('TransferDomain', () => { }) describe('transferDomain failed', () => { - it('should fail if transfer within same domain', async () => { + it('(dvm -> evm) should fail if src address is invalid', async () => { const promise = client.account.transferDomain([ { src: { - address: dvmAddr, + address: 'invalid', // invalid amount: '3@DFI', domain: TransferDomainType.DVM }, dst: { - address: dvmAddr, + address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.DVM + domain: TransferDomainType.EVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Cannot transfer inside same domain') + await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') }) - it('should fail if amount is different', async () => { + it('(dvm -> evm) should fail if dst address is invalid', async () => { const promise = client.account.transferDomain([ { src: { address: dvmAddr, - amount: '3@DFI', // diff + amount: '3@DFI', domain: TransferDomainType.DVM }, dst: { - address: dvmAddr, - amount: '46@DFI', // diff + address: 'invalid', // invalid + amount: '3@DFI', domain: TransferDomainType.EVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Source amount must be equal to destination amount') + await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') }) - it('should fail if transfer diff token', async () => { + it('(evm -> dvm) should fail if src address is invalid', async () => { const promise = client.account.transferDomain([ { src: { - address: dvmAddr, + address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.DVM + domain: TransferDomainType.EVM }, dst: { - address: dvmAddr, - amount: '3@BTC', + address: 'invalid', // invalid + amount: '3@DFI', + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') + }) + + it('(evm -> dvm) should fail if dst address is invalid', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '3@DFI', domain: TransferDomainType.EVM + }, + dst: { + address: 'invalid', // invalid + amount: '3@DFI', + domain: TransferDomainType.DVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Source token and destination token must be the same') + await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') }) - it('(dvm -> evm) should fail if source address and source domain are not match', async () => { + it('(dvm -> evm) should fail if src address is not legacy or Bech32 address in case of "DVM" domain', async () => { const promise = client.account.transferDomain([ { src: { - address: evmAddr, // <- not match + address: p2shAddr, // <- non legacy or Bech32 addres amount: '3@DFI', - domain: TransferDomainType.DVM // <- not match + domain: TransferDomainType.DVM }, dst: { address: evmAddr, @@ -128,7 +182,46 @@ describe('TransferDomain', () => { await expect(promise).rejects.toThrow('Src address must be a legacy or Bech32 address in case of "DVM" domain') }) - it('(evm -> dvm) should fail if source address and source domain are not match', async () => { + it('(evm -> dvm) should fail if dst address is not legacy or Bech32 address in case of "DVM" domain', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '1@DFI', + domain: TransferDomainType.EVM + }, + dst: { + address: p2shAddr, // <- non legacy or Bech32 addres + amount: '1@DFI', + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Dst address must be a legacy or Bech32 address in case of "DVM" domain') + }) + + it('(dvm -> evm) should fail if dst address is not ERC55 address in case of "EVM" domain', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: dvmAddr, // <- not match + amount: '3@DFI', + domain: TransferDomainType.EVM // <- not match + + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Dst address must be an ERC55 address in case of "EVM" domain') + }) + + it('(evm -> dvm) should fail if src address is not ERC55 address in case of "EVM" domain', async () => { const promise = client.account.transferDomain([ { src: { @@ -147,51 +240,224 @@ describe('TransferDomain', () => { await expect(promise).rejects.toThrow('Src address must be an ERC55 address in case of "EVM" domain') }) - it('(dvm -> evm) should fail if destination address and destination domain are not match', async () => { + it('(dvm -> evm) should fail if amount value is invalid', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: 'invalid@DFI', // invalid amount + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: 'invalid@DFI', // invalid amount + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Invalid amount') + }) + + it('(evm -> dvm) should fail if amount value is invalid', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: 'invalid@DFI', // invalid amount + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: 'invalid@DFI', // invalid amount + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Invalid amount') + }) + + it('(dvm -> evm) should fail if amount symbol is different', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '3@DFI', // not match + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '3@BTC', // not match + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Source token and destination token must be the same') + }) + + it('(evm -> dvm) should fail if amount symbol is different', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '3@DFI', // not match + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '3@BTC', // not match + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Source token and destination token must be the same') + }) + + it('(dvm -> evm) should fail if amount value is different', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '2@DFI', // not match + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '3@DFI', // not match + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Source amount must be equal to destination amount') + }) + + it('(evm -> dvm) should fail if amount value is different', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '2@DFI', // not match + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '3@BTC', // not match + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Source amount must be equal to destination amount') + }) + + it('(dvm -> evm) should fail if negative amount', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '-1@DFI', // invalid + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '-1@DFI', // invalid + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Amount out of range') + }) + + it('(evm -> dvm) should fail if negative amount', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '-1@DFI', // invalid + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '-1@DFI', // invalid + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Amount out of range') + }) + + it('(dvm -> invalid) should fail if dst domain is invalid', async () => { const promise = client.account.transferDomain([ { src: { address: dvmAddr, amount: '3@DFI', domain: TransferDomainType.DVM + }, dst: { - address: dvmAddr, // <- not match + address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.EVM // <- not match + domain: 1 // invalid } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Dst address must be an ERC55 address in case of "EVM" domain') + await expect(promise).rejects.toThrow('Unknown transfer domain aspect') }) - it('(evm -> dvm) should fail if destination address and destination domain are not match', async () => { + it('(invalid -> dvm) should fail if src domain is invalid', async () => { const promise = client.account.transferDomain([ { src: { + address: dvmAddr, + amount: '3@DFI', + domain: 1 // invalid + + }, + dst: { address: evmAddr, amount: '3@DFI', + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Invalid parameters, src argument "domain" must be either 2 (DFI token to EVM) or 3 (EVM to DFI token)') + }) + + it('(evm -> invalid) should fail if dst domain is invalid', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '3@DFI', domain: TransferDomainType.EVM }, dst: { - address: evmAddr, // <- not match + address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.DVM // <- not match + domain: 1 // invalid } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Dst address must be a legacy or Bech32 address in case of "DVM" domain') + await expect(promise).rejects.toThrow('Unknown transfer domain aspect') }) - it('(dvm -> evm) should fail if address is invalid', async () => { + it('(invalid -> evm) should fail if src domain is invalid', async () => { const promise = client.account.transferDomain([ { src: { - address: 'invalid', + address: dvmAddr, amount: '3@DFI', - domain: TransferDomainType.DVM + domain: 1 // invalid }, dst: { address: evmAddr, @@ -201,26 +467,45 @@ describe('TransferDomain', () => { } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') + await expect(promise).rejects.toThrow('Invalid parameters, src argument "domain" must be either 2 (DFI token to EVM) or 3 (EVM to DFI token)') }) - it('(evm -> dvm) should fail if address is invalid', async () => { + it('(dvm -> dvm) should fail if transfer within same domain', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM // same domain + }, + dst: { + address: dvmAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM // same domain + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('Cannot transfer inside same domain') + }) + + it('(evm -> evm) should fail if transfer within same domain', async () => { const promise = client.account.transferDomain([ { src: { address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.EVM + domain: TransferDomainType.EVM // same domain }, dst: { - address: 'invalid', + address: evmAddr, amount: '3@DFI', - domain: TransferDomainType.DVM + domain: TransferDomainType.EVM // same domain } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('recipient (invalid) does not refer to any valid address') + await expect(promise).rejects.toThrow('Cannot transfer inside same domain') }) it('(dvm -> evm) should fail if insufficient balance', async () => { @@ -228,18 +513,18 @@ describe('TransferDomain', () => { { src: { address: dvmAddr, - amount: '999@DFI', + amount: '999999@DFI', domain: TransferDomainType.DVM }, dst: { address: evmAddr, - amount: '999@DFI', + amount: '999999@DFI', domain: TransferDomainType.EVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('amount 100.00000000 is less than 999.00000000') + await expect(promise).rejects.toThrow('amount 90000.00000000 is less than 999999.00000000') }) it('(evm -> dvm) should fail if insufficient balance', async () => { @@ -261,48 +546,156 @@ describe('TransferDomain', () => { await expect(promise).rejects.toThrow(`Not enough balance in ${evmAddr} to cover "EVM" domain transfer`) }) - it('(dvm -> evm) should fail if negative amount', async () => { + it('(dvm -> evm) should fail if custom (isDAT = false) token is transferred', async () => { const promise = client.account.transferDomain([ { src: { address: dvmAddr, - amount: '-1@DFI', + amount: '3@DESC#128', domain: TransferDomainType.DVM }, dst: { address: evmAddr, - amount: '-1@DFI', + amount: '3@DESC#128', domain: TransferDomainType.EVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Amount out of range') + await expect(promise).rejects.toThrow('Non-DAT or LP tokens are not supported for transferdomain') }) - it('(evm -> dvm) should fail if negative amount', async () => { + it('(evm -> dvm) should fail if custom (isDAT = false) token is transferred', async () => { const promise = client.account.transferDomain([ { src: { address: evmAddr, - amount: '-1@DFI', + amount: '3@DESC#128', domain: TransferDomainType.EVM }, dst: { address: dvmAddr, - amount: '-1@DFI', + amount: '3@DESC#128', domain: TransferDomainType.DVM } } ]) await expect(promise).rejects.toThrow(RpcApiError) - await expect(promise).rejects.toThrow('Amount out of range') + await expect(promise).rejects.toThrow('Non-DAT or LP tokens are not supported for transferdomain') + }) + + it('(dvm -> evm) should fail if LP token is transferred', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '10@DFI-BTC', + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '10@DFI-BTC', + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow( + "RpcApiError: 'Test TransferDomainTx execution failed:\n" + + "Non-DAT or LP tokens are not supported for transferdomain', code: -32600, method: transferdomain" + ) + }) + + it('(evm -> dvm) should fail if LP token is transferred', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '10@DFI-BTC', + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '10@DFI-BTC', + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow( + "RpcApiError: 'Test TransferDomainTx execution failed:\n" + + "Non-DAT or LP tokens are not supported for transferdomain', code: -32600, method: transferdomain" + ) + }) + + it('(dvm -> evm) should fail if (duo) transfer domain', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: dvmAddr, + amount: '1@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '1@DFI', + domain: TransferDomainType.EVM + } + }, + { + src: { + address: dvmAddr, + amount: '1@DFI', + domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '1@DFI', + domain: TransferDomainType.EVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + }) + + it('(evm -> dvm) should fail if (duo) transfer domain', async () => { + const promise = client.account.transferDomain([ + { + src: { + address: evmAddr, + amount: '3@DFI', + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM + } + }, + { + src: { + address: evmAddr, + amount: '4@DFI', + domain: TransferDomainType.EVM + }, + dst: { + address: dvmAddr, + amount: '4@DFI', + domain: TransferDomainType.DVM + } + } + ]) + await expect(promise).rejects.toThrow(RpcApiError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') }) }) - it('should Transfer Domain from DVM to EVM', async () => { - const dvmAcc = await client.account.getAccount(dvmAddr) - const [dvmBalance0, tokenId0] = dvmAcc[0].split('@') + it('(dvm -> evm) should transfer domain - DFI', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const tokenId = 'DFI' + const dvmBalance0 = dvmAcc[tokenId] + const prevBalance = await getEVMBalances(client) const txid = await client.account.transferDomain([ { @@ -322,226 +715,286 @@ describe('TransferDomain', () => { expect(txid.length).toStrictEqual(64) await container.generate(1) - const dvmAcc1 = await client.account.getAccount(dvmAddr) - const [dvmBalance1, tokenId1] = dvmAcc1[0].split('@') - expect(tokenId1).toStrictEqual(tokenId0) + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const dvmBalance1 = dvmAcc1[tokenId] // check: dvm balance is transferred - expect(new BigNumber(dvmBalance1)) - .toStrictEqual(new BigNumber(dvmBalance0).minus(3)) + expect(new BigNumber(dvmBalance0)) + .toStrictEqual(new BigNumber(dvmBalance1).plus(3)) // check: evm balance = dvm balance - transferred - const withoutEthRes = await client.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - - const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)) - .toStrictEqual(new BigNumber(withEth).minus(3)) + const currentBalance = await getEVMBalances(client) + expect(new BigNumber(prevBalance)) + .toStrictEqual(new BigNumber(currentBalance).minus(3)) }) - it('should Transfer Domain from EVM to DVM', async () => { - const dvmAcc = await client.account.getAccount(dvmAddr) - const [dvmBalance0, tokenId0] = dvmAcc[0].split('@') - - const txid = await client.account.transferDomain([ + it('(dvm -> evm) should transfer domain - dToken', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const btcTokenId = 'BTC' + const btcBalance = dvmAcc[btcTokenId] + const txid1 = await client.account.transferDomain([ { src: { - address: evmAddr, - amount: '3@DFI', - domain: TransferDomainType.EVM - }, - dst: { address: dvmAddr, - amount: '3@DFI', + amount: '3@BTC', domain: TransferDomainType.DVM + }, + dst: { + address: evmAddr, + amount: '3@BTC', + domain: TransferDomainType.EVM } } ]) - expect(typeof txid).toStrictEqual('string') - expect(txid.length).toStrictEqual(64) + expect(typeof txid1).toStrictEqual('string') + expect(txid1.length).toStrictEqual(64) await container.generate(1) - const dvmAcc1 = await client.account.getAccount(dvmAddr) - const [dvmBalance1, tokenId1] = dvmAcc1[0].split('@') - expect(tokenId1).toStrictEqual(tokenId0) - expect(new BigNumber(dvmBalance1)) - .toStrictEqual(new BigNumber(dvmBalance0).plus(3)) + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const btcBalance1 = dvmAcc1[btcTokenId] - // check eth balance to be equal to zero - const withoutEthRes = await client.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)).toStrictEqual(new BigNumber(withEth)) + // check: BTC balance is transferred + expect(new BigNumber(btcBalance1)) + .toStrictEqual(new BigNumber(btcBalance).minus(3)) }) - it.skip('should (duo) Transfer Domain from DVM to EVM', async () => { - const dvmAcc = await client.account.getAccount(dvmAddr) - const [dvmBalance0, tokenId0] = dvmAcc[0].split('@') - + it('(evm -> dvm) should transfer domain - DFI', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const tokenId = 'DFI' + const dvmBalance0 = dvmAcc[tokenId] + const prevBalance = await getEVMBalances(client) const txid = await client.account.transferDomain([ { src: { - address: dvmAddr, - amount: '3@DFI', - domain: TransferDomainType.DVM - }, - dst: { address: evmAddr, amount: '3@DFI', domain: TransferDomainType.EVM - } - }, - { - src: { - address: dvmAddr, - amount: '4@DFI', - domain: TransferDomainType.DVM }, dst: { - address: evmAddr, - amount: '4@DFI', - domain: TransferDomainType.EVM + address: dvmAddr, + amount: '3@DFI', + domain: TransferDomainType.DVM } } ]) expect(typeof txid).toStrictEqual('string') expect(txid.length).toStrictEqual(64) - await container.generate(1) - - const dvmAcc1 = await client.account.getAccount(dvmAddr) - const [dvmBalance1, tokenId1] = dvmAcc1[0].split('@') - expect(tokenId1).toStrictEqual(tokenId0) - // check: dvm balance is transferred - expect(new BigNumber(dvmBalance1)) - .toStrictEqual(new BigNumber(dvmBalance0).minus(3 + 4)) + await container.generate(1) - // check: evm balance = dvm balance - transferred - const withoutEthRes = await client.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const dvmBalance1 = dvmAcc1[tokenId] + expect(new BigNumber(dvmBalance0)) + .toStrictEqual(new BigNumber(dvmBalance1).minus(3)) - const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)) - .toStrictEqual(new BigNumber(withEth).minus(3 + 4)) + // check EVM balance + const currentBalance = await getEVMBalances(client) + expect(new BigNumber(prevBalance)) + .toStrictEqual(new BigNumber(currentBalance).plus(3)) }) - it.skip('should (duo) Transfer Domain from EVM to DVM', async () => { - const dvmAcc = await client.account.getAccount(dvmAddr) - const [dvmBalance0, tokenId0] = dvmAcc[0].split('@') - - const txid = await client.account.transferDomain([ - { - src: { - address: evmAddr, - amount: '3@DFI', - domain: TransferDomainType.EVM - }, - dst: { - address: dvmAddr, - amount: '3@DFI', - domain: TransferDomainType.DVM - } - }, + it('(evm -> dvm) should transfer domain - dToken', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const btcTokenId = 'BTC' + const btcBalance = dvmAcc[btcTokenId] + const txid1 = await client.account.transferDomain([ { src: { address: evmAddr, - amount: '4@DFI', + amount: '3@BTC', domain: TransferDomainType.EVM + }, dst: { address: dvmAddr, - amount: '4@DFI', + amount: '3@BTC', domain: TransferDomainType.DVM } } ]) - expect(typeof txid).toStrictEqual('string') - expect(txid.length).toStrictEqual(64) + expect(typeof txid1).toStrictEqual('string') + expect(txid1.length).toStrictEqual(64) await container.generate(1) - const dvmAcc1 = await client.account.getAccount(dvmAddr) - const [dvmBalance1, tokenId1] = dvmAcc1[0].split('@') - expect(tokenId1).toStrictEqual(tokenId0) - expect(new BigNumber(dvmBalance1)) - .toStrictEqual(new BigNumber(dvmBalance0).plus(3 + 4)) - - // check eth balance to be equal to zero - const withoutEthRes = await client.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)).toStrictEqual(new BigNumber(withEth)) + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const btcBalance1 = dvmAcc1[btcTokenId] + + // check: BTC balance is transferred + expect(new BigNumber(btcBalance1)) + .toStrictEqual(new BigNumber(btcBalance).plus(3)) }) - it.skip('should (duo-diff) Transfer Domain from EVM to DVM and DVM to EVM', async () => { - // transfer some to evm first - await client.account.transferDomain([ + it('(dvm -> evm) should transfer domain - loan token', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const tokenId = 'AAPL' + const dvmBalance0 = dvmAcc[tokenId] + + const txid = await client.account.transferDomain([ { src: { address: dvmAddr, - amount: '3@DFI', + amount: '3@AAPL', domain: TransferDomainType.DVM }, dst: { address: evmAddr, - amount: '3@DFI', + amount: '3@AAPL', domain: TransferDomainType.EVM } } ]) + expect(typeof txid).toStrictEqual('string') + expect(txid.length).toStrictEqual(64) await container.generate(1) - const dvmAcc = await client.account.getAccount(dvmAddr) - const [dvmBalance0, tokenId0] = dvmAcc[0].split('@') + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const dvmBalance1 = dvmAcc1[tokenId] + + // check dvm balance is transferred + expect(new BigNumber(dvmBalance1)) + .toStrictEqual(new BigNumber(dvmBalance0).minus(3)) + }) + + it('(evm -> dvm) should transfer domain - loan token', async () => { + const dvmAcc = await getAccountValues(client, dvmAddr) + const tokenId = 'AAPL' + const dvmBalance0 = dvmAcc[tokenId] - // start const txid = await client.account.transferDomain([ - { - src: { - address: dvmAddr, - amount: '4@DFI', - domain: TransferDomainType.DVM - }, - dst: { - address: evmAddr, - amount: '4@DFI', - domain: TransferDomainType.EVM - } - }, { src: { address: evmAddr, - amount: '3@DFI', + amount: '3@AAPL', domain: TransferDomainType.EVM }, dst: { address: dvmAddr, - amount: '3@DFI', + amount: '3@AAPL', domain: TransferDomainType.DVM + } } ]) expect(typeof txid).toStrictEqual('string') expect(txid.length).toStrictEqual(64) - await container.generate(1) - const dvmAcc1 = await client.account.getAccount(dvmAddr) - const [dvmBalance1, tokenId1] = dvmAcc1[0].split('@') - expect(tokenId1).toStrictEqual(tokenId0) + const dvmAcc1 = await getAccountValues(client, dvmAddr) + const dvmBalance1 = dvmAcc1[tokenId] + + // check dvm balance is received expect(new BigNumber(dvmBalance1)) - .toStrictEqual(new BigNumber(dvmBalance0).plus(3 - 4)) - - // check eth balance to be equal to zero - const withoutEthRes = await client.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth).plus(4)).toStrictEqual(new BigNumber(withEth)) + .toStrictEqual(new BigNumber(dvmBalance0).plus(3)) }) }) + +async function getEVMBalances (client: ContainerAdapterClient): Promise { + const withoutEthRes = await client.account.getTokenBalances({}, false) + const [withoutEth] = withoutEthRes[0].split('@') + const withEthRes = await client.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) + const [withEth] = withEthRes[0].split('@') + return new BigNumber(withEth).minus(withoutEth) +} + +async function createLoanToken (container: MasterNodeRegTestContainer, address: string): Promise { + let vaultId + { // Oracle setup + const priceFeeds = [ + { token: 'DFI', currency: 'USD' }, + { token: 'AAPL', currency: 'USD' } + ] + const oracleId1 = await container.call('appointoracle', [address, priceFeeds, 1]) + await container.generate(1) + const timestamp1 = Math.floor(new Date().getTime() / 1000) + await container.call('setoracledata', [oracleId1, timestamp1, [ + { tokenAmount: '1@DFI', currency: 'USD' } + ], []]) + await container.call('setoracledata', [oracleId1, timestamp1, [ + { tokenAmount: '1@AAPL', currency: 'USD' } + ], []]) + await container.generate(1) + + const oracleId2 = await container.call('appointoracle', [address, priceFeeds, 1]) + await container.generate(1) + + const timestamp2 = Math.floor(new Date().getTime() / 1000) + await container.call('setoracledata', [oracleId2, timestamp2, [ + { tokenAmount: '1@DFI', currency: 'USD' } + ], []]) + await container.call('setoracledata', [oracleId2, timestamp2, [ + { tokenAmount: '1@AAPL', currency: 'USD' } + ], []]) + await container.generate(1) + + const oracleId3 = await container.call('appointoracle', [address, priceFeeds, 1]) + await container.generate(1) + + const timestamp3 = Math.floor(new Date().getTime() / 1000) + await container.call('setoracledata', [oracleId3, timestamp3, [ + { tokenAmount: '1@DFI', currency: 'USD' } + ], []]) + await container.call('setoracledata', [oracleId3, timestamp3, [ + { tokenAmount: '1@AAPL', currency: 'USD' } + ], []]) + await container.generate(1) + } + + { // Loan Scheme + await container.call('createloanscheme', [100, 1, 'default']) + await container.generate(1) + } + + { // Collateral Tokens + const blockCount = await container.getBlockCount() + await container.call('setcollateraltoken', [{ + token: 'DFI', + factor: new BigNumber(1), + fixedIntervalPriceId: 'DFI/USD', + activateAfterBlock: blockCount + 1 + }]) + await container.generate(30) + } + + { // Loan Tokens + await container.call('setloantoken', [{ + symbol: 'AAPL', + name: 'APPLE', + fixedIntervalPriceId: 'AAPL/USD', + mintable: true, + interest: new BigNumber(0.01) + }]) + + await container.generate(1) + } + + { // Vault Empty + vaultId = await container.call('createvault', [address, 'default']) + await container.generate(1) + } + + { // Vault Deposit Collateral + await container.call('deposittovault', [vaultId, address, '10000@DFI']) + await container.generate(10) + } + + { // Take Loan + await container.call('takeloan', [{ + vaultId: vaultId, + amounts: '30@AAPL' + }]) + await container.generate(1) + } +} + +async function getAccountValues (client: ContainerAdapterClient, address: string): Promise<{ [symbol: string]: string }> { + const values = await client.account.getAccount(address) + return values.reduce((res: { [symbol: string]: string }, current: string) => { + const [value, symbol] = current.split('@') + return { + ...res, + [symbol]: value + } + }, {}) +} 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 dbd5fe4d1..0a76dbf6a 100644 --- a/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts +++ b/packages/jellyfish-api-core/__tests__/category/evm/evmTx.test.ts @@ -29,6 +29,9 @@ describe('EVMTX', () => { 'v0/params/feature/evm': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/transferdomain/dvm-evm/enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', 'v0/transferdomain/dvm-evm/src-formats': ['p2pkh', 'bech32'], 'v0/transferdomain/dvm-evm/dest-formats': ['erc55'], 'v0/transferdomain/evm-dvm/src-formats': ['erc55'], @@ -37,7 +40,7 @@ describe('EVMTX', () => { } }) await container.generate(2) - dfiAddress = await container.call('getnewaddress', ['', 'bech32']) + dfiAddress = await container.call('getnewaddress', ['', 'legacy']) await container.call('utxostoaccount', [{ [dfiAddress]: '105@DFI' }]) await container.generate(1) ethAddress = await container.call('getnewaddress', ['', 'erc55']) diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts index c3c0f067b..c8f2805a3 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_transfer_domain.test.ts @@ -38,6 +38,9 @@ describe('transferDomain', () => { 'v0/params/feature/evm': 'true', 'v0/params/feature/transferdomain': 'true', 'v0/transferdomain/dvm-evm/enabled': 'true', + 'v0/transferdomain/evm-dvm/enabled': 'true', + 'v0/transferdomain/dvm-evm/dat-enabled': 'true', + 'v0/transferdomain/evm-dvm/dat-enabled': 'true', 'v0/transferdomain/dvm-evm/src-formats': ['p2pkh', 'bech32'], 'v0/transferdomain/dvm-evm/dest-formats': ['erc55'], 'v0/transferdomain/evm-dvm/src-formats': ['erc55'], @@ -55,6 +58,31 @@ describe('transferDomain', () => { evmScript = await providers.elliptic.evmScript() await testing.token.dfi({ address: dvmAddr, amount: 12 }) + await testing.token.create({ + symbol: 'BTC', + name: 'BTC', + isDAT: true, + mintable: true, + tradeable: true, + collateralAddress: dvmAddr + }) + + const txId = await testing.rpc.wallet.sendToAddress(dvmAddr, 20100) + const utxos = [{ + txid: txId, + vout: 0 + }] + await testing.rpc.token.createToken({ + collateralAddress: dvmAddr, + isDAT: false, + mintable: true, + name: 'DESC', + symbol: 'DESC', + tradeable: false + }, utxos) + await container.generate(1) + await testing.token.mint({ amount: '10', symbol: 'BTC' }) + await testing.token.mint({ amount: '10', symbol: 'DESC#128' }) await testing.generate(1) // Fund 100 DFI UTXO @@ -134,36 +162,6 @@ describe('transferDomain', () => { await expect(promise).rejects.toThrow('DeFiDRpcError: \'TransferDomainTx: Source amount must be equal to destination amount (code 16)') }) - it('should fail if transfer other than DFI token', async () => { - const transferDomain: TransferDomain = { - items: [{ - src: - { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, - amount: { - token: 1, // <- not DFI - amount: new BigNumber(3) - } - }, - dst: { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, - amount: { - token: 1, // <- not DFI - amount: new BigNumber(3) - } - } - }] - } - - const txn = await builder.account.transferDomain(transferDomain, dvmScript) - const promise = sendTransaction(testing.container, txn) - - await expect(promise).rejects.toThrow(DeFiDRpcError) - await expect(promise).rejects.toThrow('DeFiDRpcError: \'TransferDomainTx: Non-DAT or LP tokens are not supported for transferdomain (code 16)') - }) - it('(dvm -> evm) should fail if source address and source domain are not match', async () => { const transferDomain: TransferDomain = { items: [{ @@ -315,11 +313,43 @@ describe('transferDomain', () => { await expect(promise).rejects.toThrow(DeFiDRpcError) await expect(promise).rejects.toThrow('DeFiDRpcError: \'TransferDomainTx: tx must have at least one input from account owner (code 16)') }) + + it('should not transfer if custom (isDAT = false) token is transferred', async () => { + const invalidDvmScript = P2WPKH.fromAddress(RegTest, await testing.container.getNewAddress(), P2WPKH).getScript() + const transferDomain: TransferDomain = { + items: [{ + src: + { + address: dvmScript, + amount: { + token: 128, // <- DESC + amount: new BigNumber(3) + }, + domain: TRANSFER_DOMAIN_TYPE.DVM + }, + dst: { + address: evmScript, + amount: { + token: 128, // <- DESC + amount: new BigNumber(3) + }, + domain: TRANSFER_DOMAIN_TYPE.EVM + } + }] + } + + const txn = await builder.account.transferDomain(transferDomain, invalidDvmScript) + const promise = sendTransaction(testing.container, txn) + + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('Non-DAT or LP tokens are not supported for transferdomain') + }) }) it('should transfer domain from DVM to EVM', async () => { const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') + const prevBalance = await getEVMBalances(testing) const transferDomain: TransferDomain = { items: [{ @@ -373,18 +403,15 @@ describe('transferDomain', () => { .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(3)) // check: evm balance = dvm balance - transferred - const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: false }) - const [withoutEth] = withoutEthRes[0].split('@') - - const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)) - .toStrictEqual(new BigNumber(withEth).minus(3)) + const currentBalance = await getEVMBalances(testing) + expect(new BigNumber(prevBalance)) + .toStrictEqual(new BigNumber(currentBalance).minus(3)) }) it('should transfer domain from EVM to DVM', async () => { const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') + const prevBalance = await getEVMBalances(testing) const transferDomain: TransferDomain = { items: [{ @@ -433,21 +460,19 @@ describe('transferDomain', () => { const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) - // check: dev balance is updated + // check: dvm balance is updated expect(new BigNumber(dvmBalanceAfter0)) .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3)) // check evm balance to be equal to zero - const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)).toStrictEqual(new BigNumber(withEth)) + const currentBalance = await getEVMBalances(testing) + expect(new BigNumber(prevBalance)) + .toStrictEqual(new BigNumber(currentBalance).plus(3)) }) - it.skip('should (duo) transfer domain from DVM to EVM', async () => { + it('should transfer domain dToken from DVM to EVM', async () => { const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') const transferDomain: TransferDomain = { items: [{ @@ -456,43 +481,75 @@ describe('transferDomain', () => { address: dvmScript, domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 0, - amount: new BigNumber(2) - }, - data: [0] + token: 1, // <- BTC + amount: new BigNumber(3) + } }, dst: { address: evmScript, domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 0, - amount: new BigNumber(2) - }, - data: [0] + token: 1, // <- BTC + amount: new BigNumber(3) + } } - }, - { + }] + } + + const txn = await builder.account.transferDomain(transferDomain, dvmScript) + const outs = await sendTransaction(container, txn) + const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') + const expectedTransferDomainScript = `6a${encoded}` + + expect(outs.length).toStrictEqual(2) + expect(outs[0].value).toStrictEqual(0) + expect(outs[0].n).toStrictEqual(0) + expect(outs[0].tokenId).toStrictEqual(0) + expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) + expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) + expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') + + expect(outs[1].value).toEqual(expect.any(Number)) + expect(outs[1].n).toStrictEqual(1) + expect(outs[1].tokenId).toStrictEqual(0) + expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') + expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) + + const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') + expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) + + // check: dvm balance is transferred + expect(new BigNumber(dvmBalanceAfter0)) + .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(3)) + }) + + it('should transfer domain dToken from EVM to DVM', async () => { + const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) + const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[1].split('@') + + const transferDomain: TransferDomain = { + items: [{ src: { - address: dvmScript, - domain: TRANSFER_DOMAIN_TYPE.DVM, + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, amount: { - token: 0, - amount: new BigNumber(1.5) + token: 1, + amount: new BigNumber(3) }, data: [0] }, dst: { - address: evmScript, - domain: TRANSFER_DOMAIN_TYPE.EVM, + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, amount: { - token: 0, - amount: new BigNumber(1.5) + token: 1, + amount: new BigNumber(3) }, data: [0] } - } - ] + }] } const txn = await builder.account.transferDomain(transferDomain, dvmScript) @@ -515,27 +572,66 @@ describe('transferDomain', () => { expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') + const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[1].split('@') expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) - // check: dvm balance is transferred + // check: dvm balance is updated expect(new BigNumber(dvmBalanceAfter0)) - .toStrictEqual(new BigNumber(dvmBalanceBefore0).minus(2 + 1.5)) + .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3)) + }) - // check: evm balance = dvm balance - transferred - const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') + it('should fail (duo) transfer domain from DVM to EVM', async () => { + const transferDomain: TransferDomain = { + items: [{ + src: { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: [0] + }, + dst: { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(2) + }, + data: [0] + } + }, { + src: + { + address: dvmScript, + domain: TRANSFER_DOMAIN_TYPE.DVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: [0] + }, + dst: { + address: evmScript, + domain: TRANSFER_DOMAIN_TYPE.EVM, + amount: { + token: 0, + amount: new BigNumber(1.5) + }, + data: [0] + } + }] + } - const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)) - .toStrictEqual(new BigNumber(withEth).minus(2 + 1.5)) - }) + const txn = await builder.account.transferDomain(transferDomain, dvmScript) + const promise = sendTransaction(testing.container, txn) - it.skip('should (duo) transfer domain from EVM to DVM', async () => { - const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') + }) + it('should fail (duo) transfer domain from EVM to DVM', async () => { const transferDomain: TransferDomain = { items: [{ src: @@ -583,40 +679,13 @@ describe('transferDomain', () => { } const txn = await builder.account.transferDomain(transferDomain, dvmScript) - const outs = await sendTransaction(container, txn) - const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') - const expectedTransferDomainScript = `6a${encoded}` + const promise = sendTransaction(testing.container, txn) - expect(outs.length).toStrictEqual(2) - expect(outs[0].value).toStrictEqual(0) - expect(outs[0].n).toStrictEqual(0) - expect(outs[0].tokenId).toStrictEqual(0) - expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) - expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) - expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') - - expect(outs[1].value).toEqual(expect.any(Number)) - expect(outs[1].n).toStrictEqual(1) - expect(outs[1].tokenId).toStrictEqual(0) - expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') - expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) - - const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') - expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) - - expect(new BigNumber(dvmBalanceAfter0)) - .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(2 + 1.5)) - - const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - - const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth)).toStrictEqual(new BigNumber(withEth)) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') }) - it.skip('should (duo-diff) Transfer Domain from EVM to DVM and DVM to EVM', async () => { + it('should fail (duo-diff) Transfer Domain from EVM to DVM and DVM to EVM', async () => { // transfer some to evm first { const transferDomain: TransferDomain = { @@ -647,9 +716,6 @@ describe('transferDomain', () => { await sendTransaction(container, txn) } - const dvmAccBefore = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceBefore0, tokenIdBefore0] = dvmAccBefore[0].split('@') - // start const transferDomain: TransferDomain = { items: [{ @@ -698,38 +764,16 @@ describe('transferDomain', () => { } const txn = await builder.account.transferDomain(transferDomain, dvmScript) - const outs = await sendTransaction(container, txn) - const encoded: string = OP_CODES.OP_DEFI_TX_TRANSFER_DOMAIN(transferDomain).asBuffer().toString('hex') - const expectedTransferDomainScript = `6a${encoded}` - - expect(outs.length).toStrictEqual(2) - expect(outs[0].value).toStrictEqual(0) - expect(outs[0].n).toStrictEqual(0) - expect(outs[0].tokenId).toStrictEqual(0) - expect(outs[0].scriptPubKey.asm.startsWith('OP_RETURN 4466547838')).toStrictEqual(true) - expect(outs[0].scriptPubKey.hex).toStrictEqual(expectedTransferDomainScript) - expect(outs[0].scriptPubKey.type).toStrictEqual('nulldata') - - expect(outs[1].value).toEqual(expect.any(Number)) - expect(outs[1].n).toStrictEqual(1) - expect(outs[1].tokenId).toStrictEqual(0) - expect(outs[1].scriptPubKey.type).toStrictEqual('witness_v0_keyhash') - expect(outs[1].scriptPubKey.addresses[0]).toStrictEqual(dvmAddr) - - const dvmAccAfter = await testing.rpc.account.getAccount(dvmAddr) - const [dvmBalanceAfter0, tokenIdAfter0] = dvmAccAfter[0].split('@') - expect(tokenIdBefore0).toStrictEqual(tokenIdAfter0) - - // check: dvm balance is transferred - expect(new BigNumber(dvmBalanceAfter0)) - .toStrictEqual(new BigNumber(dvmBalanceBefore0).plus(3 - 4)) - - // check: evm balance = dvm balance - transferred - const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false) - const [withoutEth] = withoutEthRes[0].split('@') - - const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) - const [withEth] = withEthRes[0].split('@') - expect(new BigNumber(withoutEth).plus(4)).toStrictEqual(new BigNumber(withEth)) + const promise = sendTransaction(testing.container, txn) + await expect(promise).rejects.toThrow(DeFiDRpcError) + await expect(promise).rejects.toThrow('TransferDomain currently only supports a single transfer per transaction') }) }) + +async function getEVMBalances (testing: Testing): Promise { + const withoutEthRes = await testing.rpc.account.getTokenBalances({}, false) + const [withoutEth] = withoutEthRes[0].split('@') + const withEthRes = await testing.rpc.account.getTokenBalances({}, false, { symbolLookup: false, includeEth: true }) + const [withEth] = withEthRes[0].split('@') + return new BigNumber(withEth).minus(withoutEth) +}